From f5f7b7951b7ea5b98eb802023ec7a84d76258c84 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Thu, 9 Jul 2026 04:30:20 +0000 Subject: [PATCH] feat: add solution for 3532. Path Existence Queries in a Graph I --- .../analysis_daily_20260709.md | 61 ++++++++++++++ .../problem.md | 79 +++++++++++++++++++ .../solution_daily_20260709.go | 29 +++++++ .../solution_daily_20260709_test.go | 76 ++++++++++++++++++ 4 files changed, 245 insertions(+) create mode 100644 problems/3532-path-existence-queries-in-a-graph-i/analysis_daily_20260709.md create mode 100644 problems/3532-path-existence-queries-in-a-graph-i/problem.md create mode 100644 problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709.go create mode 100644 problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709_test.go diff --git a/problems/3532-path-existence-queries-in-a-graph-i/analysis_daily_20260709.md b/problems/3532-path-existence-queries-in-a-graph-i/analysis_daily_20260709.md new file mode 100644 index 0000000..e22cd0a --- /dev/null +++ b/problems/3532-path-existence-queries-in-a-graph-i/analysis_daily_20260709.md @@ -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. diff --git a/problems/3532-path-existence-queries-in-a-graph-i/problem.md b/problems/3532-path-existence-queries-in-a-graph-i/problem.md new file mode 100644 index 0000000..8977066 --- /dev/null +++ b/problems/3532-path-existence-queries-in-a-graph-i/problem.md @@ -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` diff --git a/problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709.go b/problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709.go new file mode 100644 index 0000000..6841452 --- /dev/null +++ b/problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709.go @@ -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 +} diff --git a/problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709_test.go b/problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709_test.go new file mode 100644 index 0000000..bd8ccc6 --- /dev/null +++ b/problems/3532-path-existence-queries-in-a-graph-i/solution_daily_20260709_test.go @@ -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) + } + }) + } +}