#3534
Hard Algorithms Path existence queries in a graph ii
Array Two Pointers Binary Search Dynamic Programming Greedy Bit Manipulation Graph Theory Sorting
26.2% acceptance
Feb 25, 2026
52
2
You are given an integer n representing the number of nodes in a graph, labeled from 0 to n - 1.
You are also given an integer array nums of length n and an integer maxDiff.
An undirected edge exists between nodes i and j if |nums[i] - nums[j]| <= maxDiff.
For each queries[i] = [ui, vi], find the minimum distance between nodes ui and vi.
If no path exists, return -1 for that query.
Solution
Rust
Time O(n * m)
Space O(n * m)
impl Solution {
pub fn path_existence_queries(
n: i32,
nums: Vec<i32>,
max_diff: i32,
queries: Vec<Vec<i32>>,
) -> Vec<i32> {
let n = n as usize;
// Sort nodes by value
let mut order: Vec<usize> = (0..n).collect();
order.sort_unstable_by_key(|&i| nums[i]);
// sorted_pos[i] = position of node i in the sorted order
let mut sorted_pos = vec![0usize; n];
for (p, &node) in order.iter().enumerate() {
sorted_pos[node] = p;
}
// In sorted order, nodes p and p+1 are connected iff
// nums[order[p+1]] - nums[order[p]] <= maxDiff
// Connected components in sorted order are contiguous ranges
// comp[p] = component id of node at sorted position p
let mut comp = vec![0u32; n];
let mut cur = 0u32;
for p in 1..n {
if nums[order[p]] - nums[order[p - 1]] > max_diff {
cur += 1;
}
comp[p] = cur;
}
// For BFS distance: nodes in same component form a clique-like structure in sorted-value space
// The minimum distance between two nodes u, v (same component):
// In the sorted order they are at positions pu and pv.
// The shortest path length = distance in the "path graph" on sorted positions
// where edge exists between p and q if |sorted_vals[p] - sorted_vals[q]| <= maxDiff.
// This is equivalent to: the min number of hops in interval graph of sorted values.
//
// Use binary lifting on sorted positions within each component.
// jump[k][p] = the furthest sorted position reachable from p in 2^k hops
// One hop from position p: can reach any q s.t. nums[order[q]] - nums[order[p]] <= maxDiff
// (going right) or nums[order[p]] - nums[order[q]] <= maxDiff (going left)
// But nums is sorted by the sort order, so from sorted position p:
// rightmost reachable = last position where sorted_val <= sorted_val[p] + maxDiff
// leftmost reachable = first position where sorted_val >= sorted_val[p] - maxDiff
let sorted_vals: Vec<i32> = order.iter().map(|&i| nums[i]).collect();
let log = 17usize;
// jump_r[k][p] = furthest right position reachable from p in 2^k hops
// jump_l[k][p] = leftmost left position reachable from p in 2^k hops
let mut jump_r = vec![vec![0usize; n]; log];
let mut jump_l = vec![vec![0usize; n]; log];
// Base: 1 hop
for p in 0..n {
// rightmost reachable: binary search for last q with sorted_vals[q] <= sorted_vals[p] + maxDiff
let target = sorted_vals[p] + max_diff;
let r = sorted_vals.partition_point(|&v| v <= target).saturating_sub(1);
jump_r[0][p] = r;
// leftmost reachable: binary search for first q with sorted_vals[q] >= sorted_vals[p] - maxDiff
let target_l = sorted_vals[p] - max_diff;
let l = sorted_vals.partition_point(|&v| v < target_l);
jump_l[0][p] = l;
}
// Fill binary lifting
for k in 1..log {
for p in 0..n {
let r1 = jump_r[k - 1][p];
jump_r[k][p] = jump_r[k - 1][r1];
let l1 = jump_l[k - 1][p];
jump_l[k][p] = jump_l[k - 1][l1];
}
}
// Distance from sorted pos pu to pv (pu <= pv):
// use binary lifting to find min hops to "cover" the gap
let dist = |pu: usize, pv: usize| -> i32 {
if pu == pv { return 0; }
if comp[pu] != comp[pv] { return -1; }
// pu < pv: count hops going right
let mut pos = pu;
let mut hops = 0i32;
for k in (0..log).rev() {
if jump_r[k][pos] < pv {
pos = jump_r[k][pos];
hops += 1 << k;
}
}
// one more hop to reach pv
if jump_r[0][pos] >= pv {
hops + 1
} else {
-1
}
};
queries
.iter()
.map(|q| {
let u = q[0] as usize;
let v = q[1] as usize;
let pu = sorted_pos[u];
let pv = sorted_pos[v];
if pu <= pv {
dist(pu, pv)
} else {
dist(pv, pu)
}
})
.collect()
}
}