Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
# 3532. Path Existence Queries in a Graph I

[LeetCode Link](https://leetcode.com/problems/path-existence-queries-in-a-graph-i/)

Difficulty: Medium
Topics: Array, Hash Table, Binary Search, Union-Find, Graph Theory
Acceptance Rate: 61.9%

## Hints

### Hint 1

Each query asks whether two nodes lie in the same connected component. That is the classic signal to reach for **connectivity** tooling — think Union-Find (DSU) or a component-labeling pass. Before jumping to code, ask yourself what special structure the input has that might make things simpler.

### Hint 2

The key given fact is that `nums` is **sorted in non-decreasing order**. Consider what an edge means in a sorted array: an edge exists between `i` and `j` when `|nums[i] - nums[j]| <= maxDiff`. If two *adjacent* elements are within `maxDiff`, they are connected. What does that tell you about the *shape* of every connected component?

### Hint 3

Because the array is sorted, if `nums[i+1] - nums[i] <= maxDiff` then nodes `i` and `i+1` are directly linked, and connectivity chains along consecutive indices. Crucially, **no edge can ever "jump over" a large gap**: if `nums[k+1] - nums[k] > maxDiff`, then any `i <= k < k+1 <= j` has `nums[j] - nums[i] >= nums[k+1] - nums[k] > maxDiff`, so no edge crosses that boundary. Therefore every connected component is a **contiguous block of indices**. Label the blocks in one pass, then each query is a simple equality check on labels.

## Approach

The naive reading suggests building a full graph and running Union-Find over all pairs — but `n` can be up to 10^5, so we cannot afford to examine all pairs. The sorted-array structure gives us something much cheaper.

**Step 1 — Label contiguous components.**
Walk through the array from left to right, maintaining a component id (starting at 0). For each `i` from `1` to `n-1`:

- If `nums[i] - nums[i-1] <= maxDiff`, node `i` belongs to the same component as node `i-1` (there is a direct edge).
- Otherwise, the gap is too large; start a new component by incrementing the id.

Store this id in a `comp` array, so `comp[i]` is the component label of node `i`.

Why is this correct? In a sorted array, differences between adjacent elements are the smallest possible steps. If every adjacent gap inside a range is `<= maxDiff`, the whole range is connected through a chain of edges. And if some adjacent gap exceeds `maxDiff`, nothing on the left can connect to anything on the right (any cross-boundary difference is at least that gap). So components are exactly the maximal contiguous runs where adjacent gaps stay within `maxDiff`.

**Step 2 — Answer each query in O(1).**
Two nodes `u` and `v` are connected if and only if they carry the same component label: `comp[u] == comp[v]`. A self-query `[u, u]` is trivially `true`, and the equality check already covers it.

**Example walk-through** (`nums = [2,5,6,8]`, `maxDiff = 2`):

- Index 0: comp = 0.
- Index 1: `5 - 2 = 3 > 2` → new component → comp = 1.
- Index 2: `6 - 5 = 1 <= 2` → same component → comp = 1.
- Index 3: `8 - 6 = 2 <= 2` → same component → comp = 1.

So `comp = [0, 1, 1, 1]`. Queries `[0,1] -> 0==1? false`, `[0,2] -> false`, `[1,3] -> 1==1? true`, `[2,3] -> true`, giving `[false, false, true, true]`.

## Complexity Analysis

Time Complexity: O(n + q), where `n` is the number of nodes and `q` is the number of queries — one linear pass to label components and one constant-time lookup per query.
Space Complexity: O(n) for the component-label array (excluding the O(q) output).

## Edge Cases

- **Self-queries `[u, u]`**: Always `true`. The `comp[u] == comp[u]` comparison handles this automatically.
- **Single node (`n == 1`)**: There are no adjacent pairs to check; the lone node is its own component. Any query must be `[0, 0]`, which returns `true`.
- **`maxDiff == 0`**: Only equal adjacent values are connected. Nodes with distinct neighboring values become separate components. (Note duplicates in `nums` still connect since their difference is 0.)
- **All nodes connected**: When every adjacent gap is within `maxDiff`, there is a single component and every query is `true`.
- **No edges at all**: When every adjacent gap exceeds `maxDiff`, each node is isolated; only self-queries return `true`.
- **Large inputs**: With `n` and `q` up to 10^5, an O(n·q) or all-pairs approach would time out — the contiguous-component trick keeps it linear.
79 changes: 79 additions & 0 deletions problems/3532-path-existence-queries-in-a-graph-i/problem.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
---
number: "3532"
frontend_id: "3532"
title: "Path Existence Queries in a Graph I"
slug: "path-existence-queries-in-a-graph-i"
difficulty: "Medium"
topics:
- "Array"
- "Hash Table"
- "Binary Search"
- "Union-Find"
- "Graph Theory"
acceptance_rate: 6188.5
is_premium: false
created_at: "2026-07-09T04:28:39.972713+00:00"
fetched_at: "2026-07-09T04:28:39.972713+00:00"
link: "https://leetcode.com/problems/path-existence-queries-in-a-graph-i/"
date: "2026-07-09"
---

# 3532. Path Existence Queries in a Graph I

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` sorted in **non-decreasing** order, and an integer `maxDiff`.

An **undirected** edge exists between nodes `i` and `j` if the **absolute** difference between `nums[i]` and `nums[j]` is **at most** `maxDiff` (i.e., `|nums[i] - nums[j]| <= maxDiff`).

You are also given a 2D integer array `queries`. For each `queries[i] = [ui, vi]`, determine whether there exists a path between nodes `ui` and `vi`.

Return a boolean array `answer`, where `answer[i]` is `true` if there exists a path between `ui` and `vi` in the `ith` query and `false` otherwise.



**Example 1:**

**Input:** n = 2, nums = [1,3], maxDiff = 1, queries = [[0,0],[0,1]]

**Output:** [true,false]

**Explanation:**

* Query `[0,0]`: Node 0 has a trivial path to itself.
* Query `[0,1]`: There is no edge between Node 0 and Node 1 because `|nums[0] - nums[1]| = |1 - 3| = 2`, which is greater than `maxDiff`.
* Thus, the final answer after processing all the queries is `[true, false]`.



**Example 2:**

**Input:** n = 4, nums = [2,5,6,8], maxDiff = 2, queries = [[0,1],[0,2],[1,3],[2,3]]

**Output:** [false,false,true,true]

**Explanation:**

The resulting graph is:

![](https://assets.leetcode.com/uploads/2025/03/25/screenshot-2025-03-26-at-122249.png)

* Query `[0,1]`: There is no edge between Node 0 and Node 1 because `|nums[0] - nums[1]| = |2 - 5| = 3`, which is greater than `maxDiff`.
* Query `[0,2]`: There is no edge between Node 0 and Node 2 because `|nums[0] - nums[2]| = |2 - 6| = 4`, which is greater than `maxDiff`.
* Query `[1,3]`: There is a path between Node 1 and Node 3 through Node 2 since `|nums[1] - nums[2]| = |5 - 6| = 1` and `|nums[2] - nums[3]| = |6 - 8| = 2`, both of which are within `maxDiff`.
* Query `[2,3]`: There is an edge between Node 2 and Node 3 because `|nums[2] - nums[3]| = |6 - 8| = 2`, which is equal to `maxDiff`.
* Thus, the final answer after processing all the queries is `[false, false, true, true]`.





**Constraints:**

* `1 <= n == nums.length <= 105`
* `0 <= nums[i] <= 105`
* `nums` is sorted in **non-decreasing** order.
* `0 <= maxDiff <= 105`
* `1 <= queries.length <= 105`
* `queries[i] == [ui, vi]`
* `0 <= ui, vi < n`
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package main

// Path Existence Queries in a Graph I
//
// Because nums is sorted non-decreasing, an edge exists between adjacent nodes
// i and i+1 exactly when nums[i+1]-nums[i] <= maxDiff. No edge can ever cross a
// gap larger than maxDiff (any wider pair spans at least that gap), so every
// connected component is a contiguous block of indices.
//
// We label components in a single left-to-right pass, then answer each query in
// O(1) by comparing the two nodes' component labels.
//
// Time: O(n + q), Space: O(n).
func pathExistenceQueries(n int, nums []int, maxDiff int, queries [][]int) []bool {
comp := make([]int, n)
id := 0
for i := 1; i < n; i++ {
if nums[i]-nums[i-1] > maxDiff {
id++
}
comp[i] = id
}

answer := make([]bool, len(queries))
for i, q := range queries {
answer[i] = comp[q[0]] == comp[q[1]]
}
return answer
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package main

import (
"reflect"
"testing"
)

func TestPathExistenceQueries(t *testing.T) {
tests := []struct {
name string
n int
nums []int
maxDiff int
queries [][]int
expected []bool
}{
{
name: "example 1: self path and disconnected nodes",
n: 2,
nums: []int{1, 3},
maxDiff: 1,
queries: [][]int{{0, 0}, {0, 1}},
expected: []bool{true, false},
},
{
name: "example 2: path through intermediate node",
n: 4,
nums: []int{2, 5, 6, 8},
maxDiff: 2,
queries: [][]int{{0, 1}, {0, 2}, {1, 3}, {2, 3}},
expected: []bool{false, false, true, true},
},
{
name: "edge case: single node self query",
n: 1,
nums: []int{5},
maxDiff: 0,
queries: [][]int{{0, 0}},
expected: []bool{true},
},
{
name: "edge case: all nodes connected in one component",
n: 5,
nums: []int{1, 2, 3, 4, 5},
maxDiff: 1,
queries: [][]int{{0, 4}, {1, 3}, {2, 2}},
expected: []bool{true, true, true},
},
{
name: "edge case: maxDiff zero connects only equal duplicates",
n: 4,
nums: []int{4, 4, 7, 7},
maxDiff: 0,
queries: [][]int{{0, 1}, {2, 3}, {1, 2}, {0, 3}},
expected: []bool{true, true, false, false},
},
{
name: "edge case: no edges, every node isolated",
n: 3,
nums: []int{0, 10, 20},
maxDiff: 5,
queries: [][]int{{0, 1}, {1, 2}, {0, 2}, {2, 2}},
expected: []bool{false, false, false, true},
},
}

for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
result := pathExistenceQueries(tt.n, tt.nums, tt.maxDiff, tt.queries)
if !reflect.DeepEqual(result, tt.expected) {
t.Errorf("pathExistenceQueries(%d, %v, %d, %v) = %v, want %v",
tt.n, tt.nums, tt.maxDiff, tt.queries, result, tt.expected)
}
})
}
}