From 0f586c41dbbb7d43be132852135d1c2c9962700f Mon Sep 17 00:00:00 2001 From: rain84 Date: Thu, 23 May 2024 18:42:46 +0300 Subject: [PATCH 01/18] =?UTF-8?q?feat:=20=E2=9C=A8=20add=20TS=20solution?= =?UTF-8?q?=20to=20lc=20problem:=20No.1971?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../README_EN.md | 62 +++++++++++++++++++ .../Solution.ts | 24 +++++++ .../Solution2.ts | 28 +++++++++ 3 files changed, 114 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md b/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md index 213c7f919686d..ed496c58008f7 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md @@ -215,6 +215,35 @@ impl Solution { } ``` +#### TypeScript + +```ts +export function validPath( + n: number, + edges: number[][], + source: number, + destination: number, +): boolean { + const graph: number[][] = Array.from({ length: n }, () => []); + + for (const [a, b] of edges) { + graph[a].push(b); + graph[b].push(a); + } + + const seen = new Set(); + const dfs = (i: number) => { + if (i === destination) return true; + if (seen.has(i)) return false; + + seen.add(i); + return graph[i].some(dfs); + }; + + return dfs(source); +} +``` + @@ -309,6 +338,39 @@ func validPath(n int, edges [][]int, source int, destination int) bool { } ``` +#### TypeScript + +```ts +export function validPath( + n: number, + edges: number[][], + source: number, + destination: number, +): boolean { + const graph: number[][] = Array.from({ length: n }, () => []); + + for (const [a, b] of edges) { + graph[a].push(b); + graph[b].push(a); + } + + const seen = new Set(); + const q = [source]; + + while (q.length) { + const i = q.pop()!; + + if (i === destination) return true; + if (seen.has(i)) continue; + + seen.add(i); + q.push(...graph[i]); + } + + return false; +} +``` + diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts new file mode 100644 index 0000000000000..247bad6599a70 --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts @@ -0,0 +1,24 @@ +export function validPath( + n: number, + edges: number[][], + source: number, + destination: number, +): boolean { + const graph: number[][] = Array.from({ length: n }, () => []); + + for (const [a, b] of edges) { + graph[a].push(b); + graph[b].push(a); + } + + const seen = new Set(); + const dfs = (i: number) => { + if (i === destination) return true; + if (seen.has(i)) return false; + + seen.add(i); + return graph[i].some(dfs); + }; + + return dfs(source); +} diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts new file mode 100644 index 0000000000000..d3c11d0ee24e9 --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts @@ -0,0 +1,28 @@ +export function validPath( + n: number, + edges: number[][], + source: number, + destination: number, +): boolean { + const graph: number[][] = Array.from({ length: n }, () => []); + + for (const [a, b] of edges) { + graph[a].push(b); + graph[b].push(a); + } + + const seen = new Set(); + const q = [source]; + + while (q.length) { + const i = q.pop()!; + + if (i === destination) return true; + if (seen.has(i)) continue; + + seen.add(i); + q.push(...graph[i]); + } + + return false; +} From d7c2085b23b3d748b687aedaca41200398e04f87 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:06:31 +0800 Subject: [PATCH 02/18] Update README_EN.md --- .../README_EN.md | 360 ++++++++++++++---- 1 file changed, 288 insertions(+), 72 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md b/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md index ed496c58008f7..fb5b8c6579f28 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/README_EN.md @@ -64,7 +64,13 @@ tags: -### Solution 1 +### Solution 1: DFS + +First, we convert `edges` into an adjacency list $g$, then use DFS to determine whether there is a path from `source` to `destination`. + +During the process, we use an array `vis` to record the vertices that have been visited to avoid repeated visits. + +The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$. Where $n$ and $m$ are the number of nodes and edges, respectively. @@ -84,7 +90,7 @@ class Solution: return True return False - g = defaultdict(list) + g = [[] for _ in range(n)] for a, b in edges: g[a].append(b) g[b].append(a) @@ -96,11 +102,11 @@ class Solution: ```java class Solution { + private int destination; private boolean[] vis; private List[] g; public boolean validPath(int n, int[][] edges, int source, int destination) { - vis = new boolean[n]; g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (var e : edges) { @@ -108,16 +114,18 @@ class Solution { g[a].add(b); g[b].add(a); } - return dfs(source, destination); + vis = new boolean[n]; + this.destination = destination; + return dfs(source); } - private boolean dfs(int source, int destination) { - if (source == destination) { + private boolean dfs(int i) { + if (i == destination) { return true; } - vis[source] = true; - for (int nxt : g[source]) { - if (!vis[nxt] && dfs(nxt, destination)) { + vis[i] = true; + for (int j : g[i]) { + if (!vis[j] && dfs(j)) { return true; } } @@ -133,14 +141,16 @@ class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { vector vis(n); - vector> g(n); + vector g[n]; for (auto& e : edges) { int a = e[0], b = e[1]; g[a].emplace_back(b); g[b].emplace_back(a); } function dfs = [&](int i) -> bool { - if (i == destination) return true; + if (i == destination) { + return true; + } vis[i] = true; for (int& j : g[i]) { if (!vis[j] && dfs(j)) { @@ -182,65 +192,273 @@ func validPath(n int, edges [][]int, source int, destination int) bool { } ``` +#### TypeScript + +```ts +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const g: number[][] = Array.from({ length: n }, () => []); + for (const [a, b] of edges) { + g[a].push(b); + g[b].push(a); + } + + const vis = new Set(); + const dfs = (i: number) => { + if (i === destination) { + return true; + } + if (vis.has(i)) { + return false; + } + + vis.add(i); + return g[i].some(dfs); + }; + + return dfs(source); +} +``` + #### Rust ```rust +use std::collections::HashSet; + impl Solution { pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { - let mut disjoint_set: Vec = vec![0; n as usize]; - // Initialize the set - for i in 0..n { - disjoint_set[i as usize] = i; + let mut vis = vec![false; n as usize]; + let mut g = vec![HashSet::new(); n as usize]; + + for e in edges { + let a = e[0] as usize; + let b = e[1] as usize; + g[a].insert(b); + g[b].insert(a); } - // Traverse the edges - for p_vec in &edges { - let parent_one = Solution::find(p_vec[0], &mut disjoint_set); - let parent_two = Solution::find(p_vec[1], &mut disjoint_set); - disjoint_set[parent_one as usize] = parent_two; + dfs(source as usize, destination as usize, &mut vis, &g) + } +} + +fn dfs(i: usize, destination: usize, vis: &mut Vec, g: &Vec>) -> bool { + if i == destination { + return true; + } + vis[i] = true; + for &j in &g[i] { + if !vis[j] && dfs(j, destination, vis, g) { + return true; } + } + false +} +``` + + + + + + + +### Solution 2: BFS + +We can also use BFS to determine whether there is a path from `source` to `destination`. - let p_s = Solution::find(source, &mut disjoint_set); - let p_d = Solution::find(destination, &mut disjoint_set); +Specifically, we define a queue $q$ and initially add `source` to the queue. In addition, we use a set `vis` to record the vertices that have been visited to avoid repeated visits. - p_s == p_d +Next, we continuously take out the vertex $i$ from the queue. If $i = \text{destination}$, it means that there is a path from `source` to `destination`, and we return `true`. Otherwise, we traverse all adjacent vertices $j$ of $i$. If $j$ has not been visited, we add $j$ to the queue $q$ and mark $j$ as visited. + +Finally, if the queue is empty, it means that there is no path from `source` to `destination`, and we return `false`. + +The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$. Where $n$ and $m$ are the number of nodes and edges, respectively. + + + +#### Python3 + +```python +class Solution: + def validPath( + self, n: int, edges: List[List[int]], source: int, destination: int + ) -> bool: + g = [[] for _ in range(n)] + for a, b in edges: + g[a].append(b) + g[b].append(a) + + q = deque([source]) + vis = {source} + while q: + i = q.popleft() + if i == destination: + return True + for j in g[i]: + if j not in vis: + vis.add(j) + q.append(j) + return False +``` + +#### Java + +```java +class Solution { + public boolean validPath(int n, int[][] edges, int source, int destination) { + List[] g = new List[n]; + Arrays.setAll(g, k -> new ArrayList<>()); + for (var e : edges) { + int a = e[0], b = e[1]; + g[a].add(b); + g[b].add(a); + } + Deque q = new ArrayDeque<>(); + q.offer(source); + boolean[] vis = new boolean[n]; + vis[source] = true; + while (!q.isEmpty()) { + int i = q.poll(); + if (i == destination) { + return true; + } + for (int j : g[i]) { + if (!vis[j]) { + vis[j] = true; + q.offer(j); + } + } + } + return false; } +} +``` - pub fn find(x: i32, d_set: &mut Vec) -> i32 { - if d_set[x as usize] != x { - d_set[x as usize] = Solution::find(d_set[x as usize], d_set); +#### C++ + +```cpp +class Solution { +public: + bool validPath(int n, vector>& edges, int source, int destination) { + vector> g(n); + for (auto& e : edges) { + int a = e[0], b = e[1]; + g[a].push_back(b); + g[b].push_back(a); + } + queue q{{source}}; + vector vis(n); + vis[source] = true; + while (q.size()) { + int i = q.front(); + q.pop(); + if (i == destination) { + return true; + } + for (int j : g[i]) { + if (!vis[j]) { + vis[j] = true; + q.push(j); + } + } } - d_set[x as usize] + return false; } +}; +``` + +#### Go + +```go +func validPath(n int, edges [][]int, source int, destination int) bool { + g := make([][]int, n) + for _, e := range edges { + a, b := e[0], e[1] + g[a] = append(g[a], b) + g[b] = append(g[b], a) + } + q := []int{source} + vis := make([]bool, n) + vis[source] = true + for len(q) > 0 { + i := q[0] + q = q[1:] + if i == destination { + return true + } + for _, j := range g[i] { + if !vis[j] { + vis[j] = true + q = append(q, j) + } + } + } + return false } ``` #### TypeScript ```ts -export function validPath( - n: number, - edges: number[][], - source: number, - destination: number, -): boolean { - const graph: number[][] = Array.from({ length: n }, () => []); +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const g: number[][] = Array.from({ length: n }, () => []); for (const [a, b] of edges) { - graph[a].push(b); - graph[b].push(a); + g[a].push(b); + g[b].push(a); } - const seen = new Set(); - const dfs = (i: number) => { - if (i === destination) return true; - if (seen.has(i)) return false; + const vis = new Set(); + const q = [source]; - seen.add(i); - return graph[i].some(dfs); - }; + while (q.length) { + const i = q.pop()!; + if (i === destination) { + return true; + } + if (vis.has(i)) { + continue; + } + vis.add(i); + q.push(...g[i]); + } - return dfs(source); + return false; +} +``` + +#### Rust + +```rust +use std::collections::{ HashSet, VecDeque }; + +impl Solution { + pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { + let mut g = vec![HashSet::new(); n as usize]; + for e in edges { + let a = e[0] as usize; + let b = e[1] as usize; + g[a].insert(b); + g[b].insert(a); + } + + let mut q = VecDeque::new(); + q.push_back(source as usize); + let mut vis = vec![false; n as usize]; + vis[source as usize] = true; + + while let Some(i) = q.pop_front() { + if i == (destination as usize) { + return true; + } + for &j in &g[i] { + if !vis[j] { + vis[j] = true; + q.push_back(j); + } + } + } + + false + } } ``` @@ -250,7 +468,16 @@ export function validPath( -### Solution 2 +### Solution 3: Union-Find + +Union-Find is a tree-like data structure that, as the name suggests, is used to handle some disjoint set **merge** and **query** problems. It supports two operations: + +1. Find: Determine which subset an element belongs to. The time complexity of a single operation is $O(\alpha(n))$. +2. Union: Merge two subsets into one set. The time complexity of a single operation is $O(\alpha(n))$. + +For this problem, we can use the Union-Find set to merge the edges in `edges`, and then determine whether `source` and `destination` are in the same set. + +The time complexity is $O(n \log n + m)$ or $O(n \alpha(n) + m)$, and the space complexity is $O(n)$. Where $n$ and $m$ are the number of nodes and edges, respectively. @@ -307,10 +534,14 @@ public: vector p(n); iota(p.begin(), p.end(), 0); function find = [&](int x) -> int { - if (p[x] != x) p[x] = find(p[x]); + if (p[x] != x) { + p[x] = find(p[x]); + } return p[x]; }; - for (auto& e : edges) p[find(e[0])] = find(e[1]); + for (auto& e : edges) { + p[find(e[0])] = find(e[1]); + } return find(source) == find(destination); } }; @@ -341,33 +572,18 @@ func validPath(n int, edges [][]int, source int, destination int) bool { #### TypeScript ```ts -export function validPath( - n: number, - edges: number[][], - source: number, - destination: number, -): boolean { - const graph: number[][] = Array.from({ length: n }, () => []); - +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const p: number[] = Array.from({ length: n }, (_, i) => i); + const find = (x: number): number => { + if (p[x] !== x) { + p[x] = find(p[x]); + } + return p[x]; + }; for (const [a, b] of edges) { - graph[a].push(b); - graph[b].push(a); + p[find(a)] = find(b); } - - const seen = new Set(); - const q = [source]; - - while (q.length) { - const i = q.pop()!; - - if (i === destination) return true; - if (seen.has(i)) continue; - - seen.add(i); - q.push(...graph[i]); - } - - return false; + return find(source) === find(destination); } ``` From 8d8e7521b1a603a3e8b6dc23a3634071bc17de80 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:07:06 +0800 Subject: [PATCH 03/18] Update README.md --- .../README.md | 393 ++++++++++++------ 1 file changed, 274 insertions(+), 119 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/README.md b/solution/1900-1999/1971.Find if Path Exists in Graph/README.md index 154ef49a2e3c9..09af4c3bf6d24 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/README.md +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/README.md @@ -72,7 +72,7 @@ tags: 过程中,我们用数组 `vis` 记录已经访问过的顶点,避免重复访问。 -时间复杂度 $O(n + m)$,其中 $n$ 和 $m$ 分别是节点数和边数。 +时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别是节点数和边数。 @@ -92,7 +92,7 @@ class Solution: return True return False - g = defaultdict(list) + g = [[] for _ in range(n)] for a, b in edges: g[a].append(b) g[b].append(a) @@ -104,11 +104,11 @@ class Solution: ```java class Solution { + private int destination; private boolean[] vis; private List[] g; public boolean validPath(int n, int[][] edges, int source, int destination) { - vis = new boolean[n]; g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (var e : edges) { @@ -116,16 +116,18 @@ class Solution { g[a].add(b); g[b].add(a); } - return dfs(source, destination); + vis = new boolean[n]; + this.destination = destination; + return dfs(source); } - private boolean dfs(int source, int destination) { - if (source == destination) { + private boolean dfs(int i) { + if (i == destination) { return true; } - vis[source] = true; - for (int nxt : g[source]) { - if (!vis[nxt] && dfs(nxt, destination)) { + vis[i] = true; + for (int j : g[i]) { + if (!vis[j] && dfs(j)) { return true; } } @@ -141,14 +143,16 @@ class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { vector vis(n); - vector> g(n); + vector g[n]; for (auto& e : edges) { int a = e[0], b = e[1]; g[a].emplace_back(b); g[b].emplace_back(a); } function dfs = [&](int i) -> bool { - if (i == destination) return true; + if (i == destination) { + return true; + } vis[i] = true; for (int& j : g[i]) { if (!vis[j] && dfs(j)) { @@ -190,36 +194,65 @@ func validPath(n int, edges [][]int, source int, destination int) bool { } ``` +#### TypeScript + +```ts +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const g: number[][] = Array.from({ length: n }, () => []); + for (const [a, b] of edges) { + g[a].push(b); + g[b].push(a); + } + + const vis = new Set(); + const dfs = (i: number) => { + if (i === destination) { + return true; + } + if (vis.has(i)) { + return false; + } + + vis.add(i); + return g[i].some(dfs); + }; + + return dfs(source); +} +``` + #### Rust ```rust +use std::collections::HashSet; + impl Solution { pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { - let mut disjoint_set: Vec = vec![0; n as usize]; - // Initialize the set - for i in 0..n { - disjoint_set[i as usize] = i; + let mut vis = vec![false; n as usize]; + let mut g = vec![HashSet::new(); n as usize]; + + for e in edges { + let a = e[0] as usize; + let b = e[1] as usize; + g[a].insert(b); + g[b].insert(a); } - // Traverse the edges - for p_vec in &edges { - let parent_one = Solution::find(p_vec[0], &mut disjoint_set); - let parent_two = Solution::find(p_vec[1], &mut disjoint_set); - disjoint_set[parent_one as usize] = parent_two; - } - - let p_s = Solution::find(source, &mut disjoint_set); - let p_d = Solution::find(destination, &mut disjoint_set); - - p_s == p_d + dfs(source as usize, destination as usize, &mut vis, &g) } +} - pub fn find(x: i32, d_set: &mut Vec) -> i32 { - if d_set[x as usize] != x { - d_set[x as usize] = Solution::find(d_set[x as usize], d_set); +fn dfs(i: usize, destination: usize, vis: &mut Vec, g: &Vec>) -> bool { + if i == destination { + return true; + } + vis[i] = true; + for &j in &g[i] { + if !vis[j] && dfs(j, destination, vis, g) { + return true; } - d_set[x as usize] } + false } ``` @@ -229,125 +262,225 @@ impl Solution { -### 方法二:并查集 - -判断图中两个节点是否连通,一种比较简单直接的方法是使用并查集。 +### 方法二:BFS -先构建并查集,然后将每条边的两个节点合并。 +我们也可以使用 BFS,判断是否存在从 `source` 到 `destination` 的路径。 -最后查询 `source` 和 `destination` 的祖宗节点是否相同,相同则说明两个节点连通。 +具体地,我们定义一个队列 $q$,初始时将 `source` 加入队列。另外,我们用一个集合 `vis` 记录已经访问过的顶点,避免重复访问。 -时间复杂度 $O(n + m \times \alpha(m))$,空间复杂度 $O(n)$。其中 $n$ 和 $m$ 分别是节点数和边数。 +接下来,我们不断从队列中取出顶点 $i$,如果 $i = \text{destination}$,则说明存在从 `source` 到 `destination` 的路径,返回 `true`。否则,我们遍历 $i$ 的所有邻接顶点 $j$,如果 $j$ 没有被访问过,我们将 $j$ 加入队列 $q$,并且标记 $j$ 为已访问。 -附并查集相关介绍以及常用模板: +最后,如果队列为空,说明不存在从 `source` 到 `destination` 的路径,返回 `false`。 -并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的**合并**及**查询**问题。 它支持两种操作: +时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别是节点数和边数。 -1. 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 $O(\alpha(n))$ -1. 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 $O(\alpha(n))$ + -其中 $\alpha$ 为阿克曼函数的反函数,其增长极其缓慢,也就是说其单次操作的平均运行时间可以认为是一个很小的常数。 +#### Python3 -以下是并查集的常用模板,需要熟练掌握。其中: +```python +class Solution: + def validPath( + self, n: int, edges: List[List[int]], source: int, destination: int + ) -> bool: + g = [[] for _ in range(n)] + for a, b in edges: + g[a].append(b) + g[b].append(a) -- `n` 表示节点数 -- `p` 存储每个点的父节点,初始时每个点的父节点都是自己 -- `size` 只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量 -- `find(x)` 函数用于查找 $x$ 所在集合的祖宗节点 -- `union(a, b)` 函数用于合并 $a$ 和 $b$ 所在的集合 + q = deque([source]) + vis = {source} + while q: + i = q.popleft() + if i == destination: + return True + for j in g[i]: + if j not in vis: + vis.add(j) + q.append(j) + return False +``` -```python [sol1-Python3 模板] -p = list(range(n)) -size = [1] * n +#### Java -def find(x): - if p[x] != x: - # 路径压缩 - p[x] = find(p[x]) - return p[x] +```java +class Solution { + public boolean validPath(int n, int[][] edges, int source, int destination) { + List[] g = new List[n]; + Arrays.setAll(g, k -> new ArrayList<>()); + for (var e : edges) { + int a = e[0], b = e[1]; + g[a].add(b); + g[b].add(a); + } + Deque q = new ArrayDeque<>(); + q.offer(source); + boolean[] vis = new boolean[n]; + vis[source] = true; + while (!q.isEmpty()) { + int i = q.poll(); + if (i == destination) { + return true; + } + for (int j : g[i]) { + if (!vis[j]) { + vis[j] = true; + q.offer(j); + } + } + } + return false; + } +} +``` +#### C++ -def union(a, b): - pa, pb = find(a), find(b) - if pa == pb: - return - p[pa] = pb - size[pb] += size[pa] +```cpp +class Solution { +public: + bool validPath(int n, vector>& edges, int source, int destination) { + vector> g(n); + for (auto& e : edges) { + int a = e[0], b = e[1]; + g[a].push_back(b); + g[b].push_back(a); + } + queue q{{source}}; + vector vis(n); + vis[source] = true; + while (q.size()) { + int i = q.front(); + q.pop(); + if (i == destination) { + return true; + } + for (int j : g[i]) { + if (!vis[j]) { + vis[j] = true; + q.push(j); + } + } + } + return false; + } +}; ``` -```java [sol1-Java 模板] -int[] p = new int[n]; -int[] size = new int[n]; -for (int i = 0; i < n; ++i) { - p[i] = i; - size[i] = 1; -} +#### Go -int find(int x) { - if (p[x] != x) { - // 路径压缩 - p[x] = find(p[x]); - } - return p[x]; +```go +func validPath(n int, edges [][]int, source int, destination int) bool { + g := make([][]int, n) + for _, e := range edges { + a, b := e[0], e[1] + g[a] = append(g[a], b) + g[b] = append(g[b], a) + } + q := []int{source} + vis := make([]bool, n) + vis[source] = true + for len(q) > 0 { + i := q[0] + q = q[1:] + if i == destination { + return true + } + for _, j := range g[i] { + if !vis[j] { + vis[j] = true + q = append(q, j) + } + } + } + return false } +``` + +#### TypeScript -void union(int a, int b) { - int pa = find(a), pb = find(b); - if (pa == pb) { - return; +```ts +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const g: number[][] = Array.from({ length: n }, () => []); + + for (const [a, b] of edges) { + g[a].push(b); + g[b].push(a); } - p[pa] = pb; - size[pb] += size[pa]; -} -``` -```cpp [sol1-C++ 模板] -vector p(n); -iota(p.begin(), p.end(), 0); -vector size(n, 1); + const vis = new Set(); + const q = [source]; -int find(int x) { - if (p[x] != x) { - // 路径压缩 - p[x] = find(p[x]); + while (q.length) { + const i = q.pop()!; + if (i === destination) { + return true; + } + if (vis.has(i)) { + continue; + } + vis.add(i); + q.push(...g[i]); } - return p[x]; -} -void unite(int a, int b) { - int pa = find(a), pb = find(b); - if (pa == pb) return; - p[pa] = pb; - size[pb] += size[pa]; + return false; } ``` -```go [sol1-Go 模板] -p := make([]int, n) -size := make([]int, n) -for i := range p { - p[i] = i - size[i] = 1 -} +#### Rust -func find(x int) int { - if p[x] != x { - // 路径压缩 - p[x] = find(p[x]) - } - return p[x] -} +```rust +use std::collections::{ HashSet, VecDeque }; + +impl Solution { + pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { + let mut g = vec![HashSet::new(); n as usize]; + for e in edges { + let a = e[0] as usize; + let b = e[1] as usize; + g[a].insert(b); + g[b].insert(a); + } -func union(a, b int) { - pa, pb := find(a), find(b) - if pa == pb { - return + let mut q = VecDeque::new(); + q.push_back(source as usize); + let mut vis = vec![false; n as usize]; + vis[source as usize] = true; + + while let Some(i) = q.pop_front() { + if i == (destination as usize) { + return true; + } + for &j in &g[i] { + if !vis[j] { + vis[j] = true; + q.push_back(j); + } + } + } + + false } - p[pa] = pb - size[pb] += size[pa] } ``` + + + + + + +### 方法三:并查集 + +并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的**合并**及**查询**问题。 它支持两种操作: + +1. 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 $O(\alpha(n))$ +1. 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 $O(\alpha(n))$ + +对于本题,我们可以利用并查集,将 `edges` 中的边进行合并,然后判断 `source` 和 `destination` 是否在同一个集合中。 + +时间复杂度 $O(n \log n + m)$ 或 $O(n \alpha(n) + m)$,空间复杂度 $O(n)$。其中 $n$ 和 $m$ 分别是节点数和边数。 + #### Python3 @@ -403,10 +536,14 @@ public: vector p(n); iota(p.begin(), p.end(), 0); function find = [&](int x) -> int { - if (p[x] != x) p[x] = find(p[x]); + if (p[x] != x) { + p[x] = find(p[x]); + } return p[x]; }; - for (auto& e : edges) p[find(e[0])] = find(e[1]); + for (auto& e : edges) { + p[find(e[0])] = find(e[1]); + } return find(source) == find(destination); } }; @@ -434,6 +571,24 @@ func validPath(n int, edges [][]int, source int, destination int) bool { } ``` +#### TypeScript + +```ts +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const p: number[] = Array.from({ length: n }, (_, i) => i); + const find = (x: number): number => { + if (p[x] !== x) { + p[x] = find(p[x]); + } + return p[x]; + }; + for (const [a, b] of edges) { + p[find(a)] = find(b); + } + return find(source) === find(destination); +} +``` + From b589211c3bda1ae4c09d0d419f9c8baa52ad677f Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:07:27 +0800 Subject: [PATCH 04/18] Update Solution.py --- .../1900-1999/1971.Find if Path Exists in Graph/Solution.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.py b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.py index 8bc7f3c01056c..be95b5cd3e82e 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.py +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.py @@ -11,7 +11,7 @@ def dfs(i): return True return False - g = defaultdict(list) + g = [[] for _ in range(n)] for a, b in edges: g[a].append(b) g[b].append(a) From 603c0dccacb4dd0dad5d0b06b23e5aa7b7d681a2 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:07:43 +0800 Subject: [PATCH 05/18] Update Solution.java --- .../Solution.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.java b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.java index d17566422e991..82f9d740fbbe7 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.java +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.java @@ -1,9 +1,9 @@ class Solution { + private int destination; private boolean[] vis; private List[] g; public boolean validPath(int n, int[][] edges, int source, int destination) { - vis = new boolean[n]; g = new List[n]; Arrays.setAll(g, k -> new ArrayList<>()); for (var e : edges) { @@ -11,19 +11,21 @@ public boolean validPath(int n, int[][] edges, int source, int destination) { g[a].add(b); g[b].add(a); } - return dfs(source, destination); + vis = new boolean[n]; + this.destination = destination; + return dfs(source); } - private boolean dfs(int source, int destination) { - if (source == destination) { + private boolean dfs(int i) { + if (i == destination) { return true; } - vis[source] = true; - for (int nxt : g[source]) { - if (!vis[nxt] && dfs(nxt, destination)) { + vis[i] = true; + for (int j : g[i]) { + if (!vis[j] && dfs(j)) { return true; } } return false; } -} \ No newline at end of file +} From 404184c7f1dd364dc8e0bad104f599f6e15c9b7d Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:07:53 +0800 Subject: [PATCH 06/18] Update Solution.cpp --- .../1971.Find if Path Exists in Graph/Solution.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.cpp b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.cpp index df3683cf97ec3..af46f0849ea14 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.cpp +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.cpp @@ -2,14 +2,16 @@ class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { vector vis(n); - vector> g(n); + vector g[n]; for (auto& e : edges) { int a = e[0], b = e[1]; g[a].emplace_back(b); g[b].emplace_back(a); } function dfs = [&](int i) -> bool { - if (i == destination) return true; + if (i == destination) { + return true; + } vis[i] = true; for (int& j : g[i]) { if (!vis[j] && dfs(j)) { @@ -20,4 +22,4 @@ class Solution { }; return dfs(source); } -}; \ No newline at end of file +}; From 28c8499a85f6dd3d52733b1de9bd957a142006d4 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:08:08 +0800 Subject: [PATCH 07/18] Update Solution.ts --- .../Solution.ts | 28 +++++++++---------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts index 247bad6599a70..649dc030166ae 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts @@ -1,23 +1,21 @@ -export function validPath( - n: number, - edges: number[][], - source: number, - destination: number, -): boolean { - const graph: number[][] = Array.from({ length: n }, () => []); - +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const g: number[][] = Array.from({ length: n }, () => []); for (const [a, b] of edges) { - graph[a].push(b); - graph[b].push(a); + g[a].push(b); + g[b].push(a); } - const seen = new Set(); + const vis = new Set(); const dfs = (i: number) => { - if (i === destination) return true; - if (seen.has(i)) return false; + if (i === destination) { + return true; + } + if (vis.has(i)) { + return false; + } - seen.add(i); - return graph[i].some(dfs); + vis.add(i); + return g[i].some(dfs); }; return dfs(source); From b94fe593f909c773b432f06cc130ed11493cba86 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:08:19 +0800 Subject: [PATCH 08/18] Update Solution2.py --- .../Solution2.py | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.py b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.py index a6d19f52c4fcd..539791f3fc81e 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.py +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.py @@ -2,12 +2,19 @@ class Solution: def validPath( self, n: int, edges: List[List[int]], source: int, destination: int ) -> bool: - def find(x): - if p[x] != x: - p[x] = find(p[x]) - return p[x] + g = [[] for _ in range(n)] + for a, b in edges: + g[a].append(b) + g[b].append(a) - p = list(range(n)) - for u, v in edges: - p[find(u)] = find(v) - return find(source) == find(destination) + q = deque([source]) + vis = {source} + while q: + i = q.popleft() + if i == destination: + return True + for j in g[i]: + if j not in vis: + vis.add(j) + q.append(j) + return False From f216f1ef00f0347cc69b800610be307ac365d323 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:08:31 +0800 Subject: [PATCH 09/18] Update Solution2.java --- .../Solution2.java | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.java b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.java index 6cf85193fe6a3..f7f993924cc77 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.java +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.java @@ -1,21 +1,28 @@ class Solution { - private int[] p; - public boolean validPath(int n, int[][] edges, int source, int destination) { - p = new int[n]; - for (int i = 0; i < n; ++i) { - p[i] = i; + List[] g = new List[n]; + Arrays.setAll(g, k -> new ArrayList<>()); + for (var e : edges) { + int a = e[0], b = e[1]; + g[a].add(b); + g[b].add(a); } - for (int[] e : edges) { - p[find(e[0])] = find(e[1]); + Deque q = new ArrayDeque<>(); + q.offer(source); + boolean[] vis = new boolean[n]; + vis[source] = true; + while (!q.isEmpty()) { + int i = q.poll(); + if (i == destination) { + return true; + } + for (int j : g[i]) { + if (!vis[j]) { + vis[j] = true; + q.offer(j); + } + } } - return find(source) == find(destination); + return false; } - - private int find(int x) { - if (p[x] != x) { - p[x] = find(p[x]); - } - return p[x]; - } -} \ No newline at end of file +} From 508b202206f8cded98147da6610b976a60448314 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:08:41 +0800 Subject: [PATCH 10/18] Update Solution2.cpp --- .../Solution2.cpp | 33 ++++++++++++++----- 1 file changed, 24 insertions(+), 9 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.cpp b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.cpp index b8cae57d8318b..c2c1b73efe227 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.cpp +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.cpp @@ -1,13 +1,28 @@ class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { - vector p(n); - iota(p.begin(), p.end(), 0); - function find = [&](int x) -> int { - if (p[x] != x) p[x] = find(p[x]); - return p[x]; - }; - for (auto& e : edges) p[find(e[0])] = find(e[1]); - return find(source) == find(destination); + vector> g(n); + for (auto& e : edges) { + int a = e[0], b = e[1]; + g[a].push_back(b); + g[b].push_back(a); + } + queue q{{source}}; + vector vis(n); + vis[source] = true; + while (q.size()) { + int i = q.front(); + q.pop(); + if (i == destination) { + return true; + } + for (int j : g[i]) { + if (!vis[j]) { + vis[j] = true; + q.push(j); + } + } + } + return false; } -}; \ No newline at end of file +}; From 2b955bb64e13f49ac296107075d0256d0c09598c Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:08:58 +0800 Subject: [PATCH 11/18] Update Solution2.go --- .../Solution2.go | 34 ++++++++++++------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.go b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.go index dcdedbee8d23f..6f3ca4c47530a 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.go +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.go @@ -1,17 +1,25 @@ func validPath(n int, edges [][]int, source int, destination int) bool { - p := make([]int, n) - for i := range p { - p[i] = i + g := make([][]int, n) + for _, e := range edges { + a, b := e[0], e[1] + g[a] = append(g[a], b) + g[b] = append(g[b], a) } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) + q := []int{source} + vis := make([]bool, n) + vis[source] = true + for len(q) > 0 { + i := q[0] + q = q[1:] + if i == destination { + return true + } + for _, j := range g[i] { + if !vis[j] { + vis[j] = true + q = append(q, j) + } } - return p[x] - } - for _, e := range edges { - p[find(e[0])] = find(e[1]) } - return find(source) == find(destination) -} \ No newline at end of file + return false +} From 40c7d6c2ddaae601995608871755e73f641271a7 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:09:10 +0800 Subject: [PATCH 12/18] Update Solution2.ts --- .../Solution2.ts | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts index d3c11d0ee24e9..1d741f6bb04f2 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts @@ -1,27 +1,24 @@ -export function validPath( - n: number, - edges: number[][], - source: number, - destination: number, -): boolean { - const graph: number[][] = Array.from({ length: n }, () => []); +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const g: number[][] = Array.from({ length: n }, () => []); for (const [a, b] of edges) { - graph[a].push(b); - graph[b].push(a); + g[a].push(b); + g[b].push(a); } - const seen = new Set(); + const vis = new Set(); const q = [source]; while (q.length) { const i = q.pop()!; - - if (i === destination) return true; - if (seen.has(i)) continue; - - seen.add(i); - q.push(...graph[i]); + if (i === destination) { + return true; + } + if (vis.has(i)) { + continue; + } + vis.add(i); + q.push(...g[i]); } return false; From aa0358755ba52b6f1f81805cf7a5b4735f083258 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:09:27 +0800 Subject: [PATCH 13/18] Create Solution2.rs --- .../Solution2.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.rs diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.rs b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.rs new file mode 100644 index 0000000000000..c1be44eb3a894 --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.rs @@ -0,0 +1,32 @@ +use std::collections::{ HashSet, VecDeque }; + +impl Solution { + pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { + let mut g = vec![HashSet::new(); n as usize]; + for e in edges { + let a = e[0] as usize; + let b = e[1] as usize; + g[a].insert(b); + g[b].insert(a); + } + + let mut q = VecDeque::new(); + q.push_back(source as usize); + let mut vis = vec![false; n as usize]; + vis[source as usize] = true; + + while let Some(i) = q.pop_front() { + if i == (destination as usize) { + return true; + } + for &j in &g[i] { + if !vis[j] { + vis[j] = true; + q.push_back(j); + } + } + } + + false + } +} From af2dfe0f029e0180445929e983d05e7b120c7735 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:09:38 +0800 Subject: [PATCH 14/18] Create Solution3.py --- .../1971.Find if Path Exists in Graph/Solution3.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.py diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.py b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.py new file mode 100644 index 0000000000000..a6d19f52c4fcd --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.py @@ -0,0 +1,13 @@ +class Solution: + def validPath( + self, n: int, edges: List[List[int]], source: int, destination: int + ) -> bool: + def find(x): + if p[x] != x: + p[x] = find(p[x]) + return p[x] + + p = list(range(n)) + for u, v in edges: + p[find(u)] = find(v) + return find(source) == find(destination) From d1a340a098de95f2b03d0142c9c2e78697efa8be Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:09:48 +0800 Subject: [PATCH 15/18] Create Solution3.java --- .../Solution3.java | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.java diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.java b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.java new file mode 100644 index 0000000000000..640d32b33bba7 --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.java @@ -0,0 +1,21 @@ +class Solution { + private int[] p; + + public boolean validPath(int n, int[][] edges, int source, int destination) { + p = new int[n]; + for (int i = 0; i < n; ++i) { + p[i] = i; + } + for (int[] e : edges) { + p[find(e[0])] = find(e[1]); + } + return find(source) == find(destination); + } + + private int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } +} From ffcced934f96fdb5ca70322cb32c1d6344911904 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:10:00 +0800 Subject: [PATCH 16/18] Create Solution3.cpp --- .../Solution3.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.cpp diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.cpp b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.cpp new file mode 100644 index 0000000000000..a84222dead5ea --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.cpp @@ -0,0 +1,17 @@ +class Solution { +public: + bool validPath(int n, vector>& edges, int source, int destination) { + vector p(n); + iota(p.begin(), p.end(), 0); + function find = [&](int x) -> int { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + }; + for (auto& e : edges) { + p[find(e[0])] = find(e[1]); + } + return find(source) == find(destination); + } +}; From 18660ea58a22a029233a42bfd3ee39dc49b08d22 Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:10:12 +0800 Subject: [PATCH 17/18] Create Solution3.go --- .../Solution3.go | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.go diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.go b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.go new file mode 100644 index 0000000000000..9f30955e375de --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.go @@ -0,0 +1,17 @@ +func validPath(n int, edges [][]int, source int, destination int) bool { + p := make([]int, n) + for i := range p { + p[i] = i + } + var find func(x int) int + find = func(x int) int { + if p[x] != x { + p[x] = find(p[x]) + } + return p[x] + } + for _, e := range edges { + p[find(e[0])] = find(e[1]) + } + return find(source) == find(destination) +} From 29953c3c53b95c8045b5cd107a5c5385c81bb06c Mon Sep 17 00:00:00 2001 From: Libin YANG Date: Fri, 24 May 2024 09:10:25 +0800 Subject: [PATCH 18/18] Create Solution3.ts --- .../1971.Find if Path Exists in Graph/Solution3.ts | 13 +++++++++++++ 1 file changed, 13 insertions(+) create mode 100644 solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.ts diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.ts b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.ts new file mode 100644 index 0000000000000..76c3c8e38adc6 --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.ts @@ -0,0 +1,13 @@ +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const p: number[] = Array.from({ length: n }, (_, i) => i); + const find = (x: number): number => { + if (p[x] !== x) { + p[x] = find(p[x]); + } + return p[x]; + }; + for (const [a, b] of edges) { + p[find(a)] = find(b); + } + return find(source) === find(destination); +}