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 b31c2f87b0253..495d78e0fb30e 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 @@ -33,7 +33,7 @@ tags: 输入:n = 3, edges = [[0,1],[1,2],[2,0]], source = 0, destination = 2 输出:true 解释:存在由顶点 0 到顶点 2 的路径: -- 0 → 1 → 2 +- 0 → 1 → 2 - 0 → 2 @@ -68,9 +68,9 @@ tags: ### 方法一:DFS -我们先将 `edges` 转换成邻接表 $g$,然后使用 DFS,判断是否存在从 `source` 到 `destination` 的路径。 +我们首先将 $\textit{edges}$ 转换成邻接表 $g$,然后使用 DFS,判断是否存在从 $\textit{source}$ 到 $\textit{destination}$ 的路径。 -过程中,我们用数组 `vis` 记录已经访问过的顶点,避免重复访问。 +过程中,我们用数组 $\textit{vis}$ 记录已经访问过的顶点,避免重复访问。 时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别是节点数和边数。 @@ -83,7 +83,7 @@ class Solution: def validPath( self, n: int, edges: List[List[int]], source: int, destination: int ) -> bool: - def dfs(i): + def dfs(i: int) -> bool: if i == destination: return True vis.add(i) @@ -93,9 +93,9 @@ class Solution: return False g = [[] for _ in range(n)] - for a, b in edges: - g[a].append(b) - g[b].append(a) + for u, v in edges: + g[u].append(v) + g[v].append(u) vis = set() return dfs(source) ``` @@ -109,15 +109,15 @@ class Solution { private List[] g; public boolean validPath(int n, int[][] edges, int source, int destination) { + this.destination = destination; + vis = new boolean[n]; g = new List[n]; - Arrays.setAll(g, k -> new ArrayList<>()); + Arrays.setAll(g, i -> new ArrayList<>()); for (var e : edges) { - int a = e[0], b = e[1]; - g[a].add(b); - g[b].add(a); + int u = e[0], v = e[1]; + g[u].add(v); + g[v].add(u); } - vis = new boolean[n]; - this.destination = destination; return dfs(source); } @@ -126,7 +126,7 @@ class Solution { return true; } vis[i] = true; - for (int j : g[i]) { + for (var j : g[i]) { if (!vis[j] && dfs(j)) { return true; } @@ -142,19 +142,19 @@ class Solution { class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { - vector vis(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); + vector vis(n); + for (const auto& e : edges) { + int u = e[0], v = e[1]; + g[u].push_back(v); + g[v].push_back(u); } function dfs = [&](int i) -> bool { if (i == destination) { return true; } vis[i] = true; - for (int& j : g[i]) { + for (int j : g[i]) { if (!vis[j] && dfs(j)) { return true; } @@ -173,9 +173,9 @@ func validPath(n int, edges [][]int, source int, destination int) bool { vis := make([]bool, n) 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) + u, v := e[0], e[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) } var dfs func(int) bool dfs = func(i int) bool { @@ -199,11 +199,10 @@ func validPath(n int, edges [][]int, source int, destination int) bool { ```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); + for (const [u, v] of edges) { + g[u].push(v); + g[v].push(u); } - const vis = new Set(); const dfs = (i: number) => { if (i === destination) { @@ -212,11 +211,9 @@ function validPath(n: number, edges: number[][], source: number, destination: nu if (vis.has(i)) { return false; } - vis.add(i); return g[i].some(dfs); }; - return dfs(source); } ``` @@ -224,35 +221,37 @@ function validPath(n: number, edges: number[][], source: number, destination: nu #### Rust ```rust -use std::collections::HashSet; - impl Solution { pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { - let mut vis = vec![false; n as usize]; - let mut g = vec![HashSet::new(); n as usize]; + let n = n as usize; + let source = source as usize; + let destination = destination as usize; + + let mut g = vec![Vec::new(); n]; + let mut vis = vec![false; n]; 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 u = e[0] as usize; + let v = e[1] as usize; + g[u].push(v); + g[v].push(u); } - 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; + fn dfs(g: &Vec>, vis: &mut Vec, i: usize, destination: usize) -> bool { + if i == destination { + return true; + } + vis[i] = true; + for &j in &g[i] { + if !vis[j] && dfs(g, vis, j, destination) { + return true; + } + } + false } + + dfs(&g, &mut vis, source, destination) } - false } ``` @@ -264,13 +263,13 @@ fn dfs(i: usize, destination: usize, vis: &mut Vec, g: &Vec ### 方法二:BFS -我们也可以使用 BFS,判断是否存在从 `source` 到 `destination` 的路径。 +我们也可以使用 BFS,判断是否存在从 $\textit{source}$ 到 $\textit{destination}$ 的路径。 -具体地,我们定义一个队列 $q$,初始时将 `source` 加入队列。另外,我们用一个集合 `vis` 记录已经访问过的顶点,避免重复访问。 +具体地,我们定义一个队列 $q$,初始时将 $\textit{source}$ 加入队列。另外,我们用一个集合 $\textit{vis}$ 记录已经访问过的顶点,避免重复访问。 -接下来,我们不断从队列中取出顶点 $i$,如果 $i = \textit{destination}$,则说明存在从 `source` 到 `destination` 的路径,返回 `true`。否则,我们遍历 $i$ 的所有邻接顶点 $j$,如果 $j$ 没有被访问过,我们将 $j$ 加入队列 $q$,并且标记 $j$ 为已访问。 +接下来,我们不断从队列中取出顶点 $i$,如果 $i = \textit{destination}$,则说明存在从 $\textit{source}$ 到 $\textit{destination}$ 的路径,返回 $\textit{true}$。否则,我们遍历 $i$ 的所有邻接顶点 $j$,如果 $j$ 没有被访问过,我们将 $j$ 加入队列 $q$,并且标记 $j$ 为已访问。 -最后,如果队列为空,说明不存在从 `source` 到 `destination` 的路径,返回 `false`。 +最后,如果队列为空,说明不存在从 $\textit{source}$ 到 $\textit{destination}$ 的路径,返回 $\textit{false}$。 时间复杂度 $O(n + m)$,空间复杂度 $O(n + m)$。其中 $n$ 和 $m$ 分别是节点数和边数。 @@ -284,10 +283,9 @@ class Solution: 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) - + for u, v in edges: + g[u].append(v) + g[v].append(u) q = deque([source]) vis = {source} while q: @@ -309,9 +307,9 @@ class Solution { 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); + int u = e[0], v = e[1]; + g[u].add(v); + g[v].add(u); } Deque q = new ArrayDeque<>(); q.offer(source); @@ -341,10 +339,10 @@ 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); + for (const auto& e : edges) { + int u = e[0], v = e[1]; + g[u].push_back(v); + g[v].push_back(u); } queue q{{source}}; vector vis(n); @@ -373,9 +371,9 @@ public: 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) + u, v := e[0], e[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) } q := []int{source} vis := make([]bool, n) @@ -402,27 +400,24 @@ func validPath(n int, edges [][]int, source int, destination int) bool { ```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); + for (const [u, v] of edges) { + g[u].push(v); + g[v].push(u); } - - const vis = new Set(); + const vis = new Set([source]); const q = [source]; - while (q.length) { const i = q.pop()!; if (i === destination) { return true; } - if (vis.has(i)) { - continue; + for (const j of g[i]) { + if (!vis.has(j)) { + vis.add(j); + q.push(j); + } } - vis.add(i); - q.push(...g[i]); } - return false; } ``` @@ -434,26 +429,30 @@ 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 n = n as usize; + let source = source as usize; + let destination = destination as usize; + + let mut g = vec![Vec::new(); n]; + for edge in edges { + let u = edge[0] as usize; + let v = edge[1] as usize; + g[u].push(v); + g[v].push(u); } let mut q = VecDeque::new(); - q.push_back(source as usize); - let mut vis = vec![false; n as usize]; - vis[source as usize] = true; + let mut vis = HashSet::new(); + q.push_back(source); + vis.insert(source); while let Some(i) = q.pop_front() { - if i == (destination as usize) { + if i == destination { return true; } for &j in &g[i] { - if !vis[j] { - vis[j] = true; + if !vis.contains(&j) { + vis.insert(j); q.push_back(j); } } @@ -486,65 +485,130 @@ impl Solution { #### Python3 ```python +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + 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)) + uf = UnionFind(n) for u, v in edges: - p[find(u)] = find(v) - return find(source) == find(destination) + uf.union(u, v) + return uf.find(source) == uf.find(destination) ``` #### Java ```java -class Solution { +class UnionFind { private int[] p; + private int[] size; - public boolean validPath(int n, int[][] edges, int source, int destination) { + public UnionFind(int n) { p = new int[n]; + size = new int[n]; for (int i = 0; i < n; ++i) { p[i] = i; + size[i] = 1; } - for (int[] e : edges) { - p[find(e[0])] = find(e[1]); - } - return find(source) == find(destination); } - private int find(int x) { + public int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } + + public void union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa != pb) { + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + } + } +} + +class Solution { + public boolean validPath(int n, int[][] edges, int source, int destination) { + UnionFind uf = new UnionFind(n); + for (var e : edges) { + uf.union(e[0], e[1]); + } + return uf.find(source) == uf.find(destination); + } } ``` #### C++ ```cpp -class Solution { +class UnionFind { public: - bool validPath(int n, vector>& edges, int source, int destination) { - vector p(n); + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); iota(p.begin(), p.end(), 0); - function find = [&](int x) -> int { - if (p[x] != x) { - p[x] = find(p[x]); + } + + void unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa != pb) { + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; } - return p[x]; - }; - for (auto& e : edges) { - p[find(e[0])] = find(e[1]); } - return find(source) == find(destination); + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + bool validPath(int n, vector>& edges, int source, int destination) { + UnionFind uf(n); + for (const auto& e : edges) { + uf.unite(e[0], e[1]); + } + return uf.find(source) == uf.find(destination); } }; ``` @@ -552,40 +616,144 @@ public: #### Go ```go -func validPath(n int, edges [][]int, source int, destination int) bool { +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { p := make([]int, n) + size := make([]int, n) for i := range p { p[i] = i + size[i] = 1 } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func validPath(n int, edges [][]int, source int, destination int) bool { + uf := newUnionFind(n) for _, e := range edges { - p[find(e[0])] = find(e[1]) + uf.union(e[0], e[1]) } - return find(source) == find(destination) + return uf.find(source) == uf.find(destination) } ``` #### TypeScript ```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array(n) + .fill(0) + .map((_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + 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]); + const uf = new UnionFind(n); + edges.forEach(([u, v]) => uf.union(u, v)); + return uf.find(source) === uf.find(destination); +} +``` + +#### Rust + +```rust +struct UnionFind { + p: Vec, + size: Vec, +} + +impl UnionFind { + fn new(n: usize) -> Self { + let p = (0..n).collect(); + let size = vec![1; n]; + UnionFind { p, size } + } + + fn find(&mut self, x: usize) -> usize { + if self.p[x] != x { + self.p[x] = self.find(self.p[x]); } - return p[x]; - }; - for (const [a, b] of edges) { - p[find(a)] = find(b); + self.p[x] + } + + fn union(&mut self, a: usize, b: usize) { + let pa = self.find(a); + let pb = self.find(b); + if pa != pb { + if self.size[pa] > self.size[pb] { + self.p[pb] = pa; + self.size[pa] += self.size[pb]; + } else { + self.p[pa] = pb; + self.size[pb] += self.size[pa]; + } + } + } +} + +impl Solution { + pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { + let n = n as usize; + let mut uf = UnionFind::new(n); + + for e in edges { + let u = e[0] as usize; + let v = e[1] as usize; + uf.union(u, v); + } + + uf.find(source as usize) == uf.find(destination as usize) } - return find(source) === find(destination); } ``` 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 90c191aa18fcb..028e6c866aa70 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 @@ -66,11 +66,11 @@ tags: ### 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`. +We first convert $\textit{edges}$ into an adjacency list $g$, then use DFS to determine whether there is a path from $\textit{source}$ to $\textit{destination}$. -During the process, we use an array `vis` to record the vertices that have been visited to avoid repeated visits. +During the process, we use an array $\textit{vis}$ to record the vertices that have already been visited to avoid revisiting them. -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. +The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$. Here, $n$ and $m$ are the number of nodes and edges, respectively. @@ -81,7 +81,7 @@ class Solution: def validPath( self, n: int, edges: List[List[int]], source: int, destination: int ) -> bool: - def dfs(i): + def dfs(i: int) -> bool: if i == destination: return True vis.add(i) @@ -91,9 +91,9 @@ class Solution: return False g = [[] for _ in range(n)] - for a, b in edges: - g[a].append(b) - g[b].append(a) + for u, v in edges: + g[u].append(v) + g[v].append(u) vis = set() return dfs(source) ``` @@ -107,15 +107,15 @@ class Solution { private List[] g; public boolean validPath(int n, int[][] edges, int source, int destination) { + this.destination = destination; + vis = new boolean[n]; g = new List[n]; - Arrays.setAll(g, k -> new ArrayList<>()); + Arrays.setAll(g, i -> new ArrayList<>()); for (var e : edges) { - int a = e[0], b = e[1]; - g[a].add(b); - g[b].add(a); + int u = e[0], v = e[1]; + g[u].add(v); + g[v].add(u); } - vis = new boolean[n]; - this.destination = destination; return dfs(source); } @@ -124,7 +124,7 @@ class Solution { return true; } vis[i] = true; - for (int j : g[i]) { + for (var j : g[i]) { if (!vis[j] && dfs(j)) { return true; } @@ -140,19 +140,19 @@ class Solution { class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { - vector vis(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); + vector vis(n); + for (const auto& e : edges) { + int u = e[0], v = e[1]; + g[u].push_back(v); + g[v].push_back(u); } function dfs = [&](int i) -> bool { if (i == destination) { return true; } vis[i] = true; - for (int& j : g[i]) { + for (int j : g[i]) { if (!vis[j] && dfs(j)) { return true; } @@ -171,9 +171,9 @@ func validPath(n int, edges [][]int, source int, destination int) bool { vis := make([]bool, n) 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) + u, v := e[0], e[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) } var dfs func(int) bool dfs = func(i int) bool { @@ -197,11 +197,10 @@ func validPath(n int, edges [][]int, source int, destination int) bool { ```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); + for (const [u, v] of edges) { + g[u].push(v); + g[v].push(u); } - const vis = new Set(); const dfs = (i: number) => { if (i === destination) { @@ -210,11 +209,9 @@ function validPath(n: number, edges: number[][], source: number, destination: nu if (vis.has(i)) { return false; } - vis.add(i); return g[i].some(dfs); }; - return dfs(source); } ``` @@ -222,35 +219,37 @@ function validPath(n: number, edges: number[][], source: number, destination: nu #### Rust ```rust -use std::collections::HashSet; - impl Solution { pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { - let mut vis = vec![false; n as usize]; - let mut g = vec![HashSet::new(); n as usize]; + let n = n as usize; + let source = source as usize; + let destination = destination as usize; + + let mut g = vec![Vec::new(); n]; + let mut vis = vec![false; n]; 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 u = e[0] as usize; + let v = e[1] as usize; + g[u].push(v); + g[v].push(u); } - 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; + fn dfs(g: &Vec>, vis: &mut Vec, i: usize, destination: usize) -> bool { + if i == destination { + return true; + } + vis[i] = true; + for &j in &g[i] { + if !vis[j] && dfs(g, vis, j, destination) { + return true; + } + } + false } + + dfs(&g, &mut vis, source, destination) } - false } ``` @@ -262,15 +261,15 @@ fn dfs(i: usize, destination: usize, vis: &mut Vec, g: &Vec ### Solution 2: BFS -We can also use BFS to determine whether there is a path from `source` to `destination`. +We can also use BFS to determine whether there is a path from $\textit{source}$ to $\textit{destination}$. -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. +Specifically, we define a queue $q$, initially adding $\textit{source}$ to the queue. Additionally, we use a set $\textit{vis}$ to record the vertices that have already been visited to avoid revisiting them. -Next, we continuously take out the vertex $i$ from the queue. If $i = \textit{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. +Next, we continuously take vertices $i$ from the queue. If $i = \textit{destination}$, it means there is a path from $\textit{source}$ to $\textit{destination}$, and we return $\textit{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`. +Finally, if the queue is empty, it means there is no path from $\textit{source}$ to $\textit{destination}$, and we return $\textit{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. +The time complexity is $O(n + m)$, and the space complexity is $O(n + m)$. Here, $n$ and $m$ are the number of nodes and edges, respectively. @@ -282,10 +281,9 @@ class Solution: 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) - + for u, v in edges: + g[u].append(v) + g[v].append(u) q = deque([source]) vis = {source} while q: @@ -307,9 +305,9 @@ class Solution { 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); + int u = e[0], v = e[1]; + g[u].add(v); + g[v].add(u); } Deque q = new ArrayDeque<>(); q.offer(source); @@ -339,10 +337,10 @@ 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); + for (const auto& e : edges) { + int u = e[0], v = e[1]; + g[u].push_back(v); + g[v].push_back(u); } queue q{{source}}; vector vis(n); @@ -371,9 +369,9 @@ public: 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) + u, v := e[0], e[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) } q := []int{source} vis := make([]bool, n) @@ -400,27 +398,24 @@ func validPath(n int, edges [][]int, source int, destination int) bool { ```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); + for (const [u, v] of edges) { + g[u].push(v); + g[v].push(u); } - - const vis = new Set(); + const vis = new Set([source]); const q = [source]; - while (q.length) { const i = q.pop()!; if (i === destination) { return true; } - if (vis.has(i)) { - continue; + for (const j of g[i]) { + if (!vis.has(j)) { + vis.add(j); + q.push(j); + } } - vis.add(i); - q.push(...g[i]); } - return false; } ``` @@ -432,26 +427,30 @@ 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 n = n as usize; + let source = source as usize; + let destination = destination as usize; + + let mut g = vec![Vec::new(); n]; + for edge in edges { + let u = edge[0] as usize; + let v = edge[1] as usize; + g[u].push(v); + g[v].push(u); } let mut q = VecDeque::new(); - q.push_back(source as usize); - let mut vis = vec![false; n as usize]; - vis[source as usize] = true; + let mut vis = HashSet::new(); + q.push_back(source); + vis.insert(source); while let Some(i) = q.pop_front() { - if i == (destination as usize) { + if i == destination { return true; } for &j in &g[i] { - if !vis[j] { - vis[j] = true; + if !vis.contains(&j) { + vis.insert(j); q.push_back(j); } } @@ -484,65 +483,130 @@ The time complexity is $O(n \log n + m)$ or $O(n \alpha(n) + m)$, and the space #### Python3 ```python +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + 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)) + uf = UnionFind(n) for u, v in edges: - p[find(u)] = find(v) - return find(source) == find(destination) + uf.union(u, v) + return uf.find(source) == uf.find(destination) ``` #### Java ```java -class Solution { +class UnionFind { private int[] p; + private int[] size; - public boolean validPath(int n, int[][] edges, int source, int destination) { + public UnionFind(int n) { p = new int[n]; + size = new int[n]; for (int i = 0; i < n; ++i) { p[i] = i; + size[i] = 1; } - for (int[] e : edges) { - p[find(e[0])] = find(e[1]); - } - return find(source) == find(destination); } - private int find(int x) { + public int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } + + public void union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa != pb) { + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + } + } +} + +class Solution { + public boolean validPath(int n, int[][] edges, int source, int destination) { + UnionFind uf = new UnionFind(n); + for (var e : edges) { + uf.union(e[0], e[1]); + } + return uf.find(source) == uf.find(destination); + } } ``` #### C++ ```cpp -class Solution { +class UnionFind { public: - bool validPath(int n, vector>& edges, int source, int destination) { - vector p(n); + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); iota(p.begin(), p.end(), 0); - function find = [&](int x) -> int { - if (p[x] != x) { - p[x] = find(p[x]); + } + + void unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa != pb) { + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; } - return p[x]; - }; - for (auto& e : edges) { - p[find(e[0])] = find(e[1]); } - return find(source) == find(destination); + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + bool validPath(int n, vector>& edges, int source, int destination) { + UnionFind uf(n); + for (const auto& e : edges) { + uf.unite(e[0], e[1]); + } + return uf.find(source) == uf.find(destination); } }; ``` @@ -550,40 +614,144 @@ public: #### Go ```go -func validPath(n int, edges [][]int, source int, destination int) bool { +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { p := make([]int, n) + size := make([]int, n) for i := range p { p[i] = i + size[i] = 1 } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) + } + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] + } + return true +} + +func validPath(n int, edges [][]int, source int, destination int) bool { + uf := newUnionFind(n) for _, e := range edges { - p[find(e[0])] = find(e[1]) + uf.union(e[0], e[1]) } - return find(source) == find(destination) + return uf.find(source) == uf.find(destination) } ``` #### TypeScript ```ts +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array(n) + .fill(0) + .map((_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); + } + return this.p[x]; + } + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + 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]); + const uf = new UnionFind(n); + edges.forEach(([u, v]) => uf.union(u, v)); + return uf.find(source) === uf.find(destination); +} +``` + +#### Rust + +```rust +struct UnionFind { + p: Vec, + size: Vec, +} + +impl UnionFind { + fn new(n: usize) -> Self { + let p = (0..n).collect(); + let size = vec![1; n]; + UnionFind { p, size } + } + + fn find(&mut self, x: usize) -> usize { + if self.p[x] != x { + self.p[x] = self.find(self.p[x]); } - return p[x]; - }; - for (const [a, b] of edges) { - p[find(a)] = find(b); + self.p[x] + } + + fn union(&mut self, a: usize, b: usize) { + let pa = self.find(a); + let pb = self.find(b); + if pa != pb { + if self.size[pa] > self.size[pb] { + self.p[pb] = pa; + self.size[pa] += self.size[pb]; + } else { + self.p[pa] = pb; + self.size[pb] += self.size[pa]; + } + } + } +} + +impl Solution { + pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { + let n = n as usize; + let mut uf = UnionFind::new(n); + + for e in edges { + let u = e[0] as usize; + let v = e[1] as usize; + uf.union(u, v); + } + + uf.find(source as usize) == uf.find(destination as usize) } - return find(source) === find(destination); } ``` 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 af46f0849ea14..2410b6e73b964 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 @@ -1,19 +1,19 @@ class Solution { public: bool validPath(int n, vector>& edges, int source, int destination) { - vector vis(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); + vector vis(n); + for (const auto& e : edges) { + int u = e[0], v = e[1]; + g[u].push_back(v); + g[v].push_back(u); } function dfs = [&](int i) -> bool { if (i == destination) { return true; } vis[i] = true; - for (int& j : g[i]) { + for (int j : g[i]) { if (!vis[j] && dfs(j)) { return true; } diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.go b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.go index 390abc8e8b690..00da71c43f56e 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.go +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.go @@ -2,9 +2,9 @@ func validPath(n int, edges [][]int, source int, destination int) bool { vis := make([]bool, n) 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) + u, v := e[0], e[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) } var dfs func(int) bool dfs = func(i int) bool { @@ -20,4 +20,4 @@ func validPath(n int, edges [][]int, source int, destination int) bool { return false } return dfs(source) -} \ No newline at end of file +} 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 82f9d740fbbe7..99cc3052bfb9f 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 @@ -4,15 +4,15 @@ class Solution { private List[] g; public boolean validPath(int n, int[][] edges, int source, int destination) { + this.destination = destination; + vis = new boolean[n]; g = new List[n]; - Arrays.setAll(g, k -> new ArrayList<>()); + Arrays.setAll(g, i -> new ArrayList<>()); for (var e : edges) { - int a = e[0], b = e[1]; - g[a].add(b); - g[b].add(a); + int u = e[0], v = e[1]; + g[u].add(v); + g[v].add(u); } - vis = new boolean[n]; - this.destination = destination; return dfs(source); } @@ -21,7 +21,7 @@ private boolean dfs(int i) { return true; } vis[i] = true; - for (int j : g[i]) { + for (var j : g[i]) { if (!vis[j] && dfs(j)) { return true; } 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 be95b5cd3e82e..c40cb923ee69d 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 @@ -2,18 +2,16 @@ class Solution: def validPath( self, n: int, edges: List[List[int]], source: int, destination: int ) -> bool: - def dfs(i): + def dfs(i: int) -> bool: if i == destination: return True - vis.add(i) - for j in g[i]: - if j not in vis and dfs(j): - return True - return False + if i in vis: + return False + return any(dfs(j) for j in g[i]) g = [[] for _ in range(n)] - for a, b in edges: - g[a].append(b) - g[b].append(a) + for u, v in edges: + g[u].append(v) + g[v].append(u) vis = set() return dfs(source) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.rs b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.rs index 4b4a4dce68760..d2dca838da612 100644 --- a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.rs +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.rs @@ -1,28 +1,32 @@ 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; - } - - // 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 n = n as usize; + let source = source as usize; + let destination = destination as usize; - let p_s = Solution::find(source, &mut disjoint_set); - let p_d = Solution::find(destination, &mut disjoint_set); + let mut g = vec![Vec::new(); n]; + let mut vis = vec![false; n]; - p_s == p_d - } + for e in edges { + let u = e[0] as usize; + let v = e[1] as usize; + g[u].push(v); + g[v].push(u); + } - 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(g: &Vec>, vis: &mut Vec, i: usize, destination: usize) -> bool { + if i == destination { + return true; + } + vis[i] = true; + for &j in &g[i] { + if !vis[j] && dfs(g, vis, j, destination) { + return true; + } + } + false } - d_set[x as usize] + + dfs(&g, &mut vis, source, destination) } } 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 649dc030166ae..5d15d8a1f942e 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,10 +1,9 @@ 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); + for (const [u, v] of edges) { + g[u].push(v); + g[v].push(u); } - const vis = new Set(); const dfs = (i: number) => { if (i === destination) { @@ -13,10 +12,8 @@ function validPath(n: number, edges: number[][], source: number, destination: nu if (vis.has(i)) { return false; } - vis.add(i); return g[i].some(dfs); }; - return dfs(source); } 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 c2c1b73efe227..a2bd484075bde 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 @@ -2,10 +2,10 @@ 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); + for (const auto& e : edges) { + int u = e[0], v = e[1]; + g[u].push_back(v); + g[v].push_back(u); } queue q{{source}}; vector vis(n); 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 6f3ca4c47530a..41ec9b817d41e 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,9 +1,9 @@ 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) + u, v := e[0], e[1] + g[u] = append(g[u], v) + g[v] = append(g[v], u) } q := []int{source} vis := make([]bool, n) 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 f7f993924cc77..a1b0020870fcf 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 @@ -3,9 +3,9 @@ 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); + int u = e[0], v = e[1]; + g[u].add(v); + g[v].add(u); } Deque q = new ArrayDeque<>(); q.offer(source); 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 539791f3fc81e..6f8b5c7f461b3 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 @@ -3,10 +3,9 @@ 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) - + for u, v in edges: + g[u].append(v) + g[v].append(u) q = deque([source]) vis = {source} while q: 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 index 575018a990fce..d4509771b6b12 100644 --- 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 @@ -2,26 +2,30 @@ 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 n = n as usize; + let source = source as usize; + let destination = destination as usize; + + let mut g = vec![Vec::new(); n]; + for edge in edges { + let u = edge[0] as usize; + let v = edge[1] as usize; + g[u].push(v); + g[v].push(u); } let mut q = VecDeque::new(); - q.push_back(source as usize); - let mut vis = vec![false; n as usize]; - vis[source as usize] = true; + let mut vis = HashSet::new(); + q.push_back(source); + vis.insert(source); while let Some(i) = q.pop_front() { - if i == (destination as usize) { + if i == destination { return true; } for &j in &g[i] { - if !vis[j] { - vis[j] = true; + if !vis.contains(&j) { + vis.insert(j); q.push_back(j); } } 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 1d741f6bb04f2..11947531475d0 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,25 +1,22 @@ 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); + for (const [u, v] of edges) { + g[u].push(v); + g[v].push(u); } - - const vis = new Set(); + const vis = new Set([source]); const q = [source]; - while (q.length) { const i = q.pop()!; if (i === destination) { return true; } - if (vis.has(i)) { - continue; + for (const j of g[i]) { + if (!vis.has(j)) { + vis.add(j); + q.push(j); + } } - vis.add(i); - q.push(...g[i]); } - return false; } 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 index a84222dead5ea..adc9bcae5a731 100644 --- 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 @@ -1,17 +1,42 @@ -class Solution { +class UnionFind { public: - bool validPath(int n, vector>& edges, int source, int destination) { - vector p(n); + UnionFind(int n) { + p = vector(n); + size = vector(n, 1); iota(p.begin(), p.end(), 0); - function find = [&](int x) -> int { - if (p[x] != x) { - p[x] = find(p[x]); + } + + void unite(int a, int b) { + int pa = find(a), pb = find(b); + if (pa != pb) { + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; } - return p[x]; - }; - for (auto& e : edges) { - p[find(e[0])] = find(e[1]); } - return find(source) == find(destination); + } + + int find(int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + } + +private: + vector p, size; +}; + +class Solution { +public: + bool validPath(int n, vector>& edges, int source, int destination) { + UnionFind uf(n); + for (const auto& e : edges) { + uf.unite(e[0], e[1]); + } + return uf.find(source) == uf.find(destination); } }; 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 index 9f30955e375de..22bef71b22a6a 100644 --- 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 @@ -1,17 +1,43 @@ -func validPath(n int, edges [][]int, source int, destination int) bool { +type unionFind struct { + p, size []int +} + +func newUnionFind(n int) *unionFind { p := make([]int, n) + size := make([]int, n) for i := range p { p[i] = i + size[i] = 1 + } + return &unionFind{p, size} +} + +func (uf *unionFind) find(x int) int { + if uf.p[x] != x { + uf.p[x] = uf.find(uf.p[x]) } - var find func(x int) int - find = func(x int) int { - if p[x] != x { - p[x] = find(p[x]) - } - return p[x] + return uf.p[x] +} + +func (uf *unionFind) union(a, b int) bool { + pa, pb := uf.find(a), uf.find(b) + if pa == pb { + return false + } + if uf.size[pa] > uf.size[pb] { + uf.p[pb] = pa + uf.size[pa] += uf.size[pb] + } else { + uf.p[pa] = pb + uf.size[pb] += uf.size[pa] } + return true +} + +func validPath(n int, edges [][]int, source int, destination int) bool { + uf := newUnionFind(n) for _, e := range edges { - p[find(e[0])] = find(e[1]) + uf.union(e[0], e[1]) } - return find(source) == find(destination) + return uf.find(source) == uf.find(destination) } 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 index 640d32b33bba7..95d53f311390d 100644 --- 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 @@ -1,21 +1,43 @@ -class Solution { +class UnionFind { private int[] p; + private int[] size; - public boolean validPath(int n, int[][] edges, int source, int destination) { + public UnionFind(int n) { p = new int[n]; + size = new int[n]; for (int i = 0; i < n; ++i) { p[i] = i; + size[i] = 1; } - for (int[] e : edges) { - p[find(e[0])] = find(e[1]); - } - return find(source) == find(destination); } - private int find(int x) { + public int find(int x) { if (p[x] != x) { p[x] = find(p[x]); } return p[x]; } + + public void union(int a, int b) { + int pa = find(a), pb = find(b); + if (pa != pb) { + if (size[pa] > size[pb]) { + p[pb] = pa; + size[pa] += size[pb]; + } else { + p[pa] = pb; + size[pb] += size[pa]; + } + } + } +} + +class Solution { + public boolean validPath(int n, int[][] edges, int source, int destination) { + UnionFind uf = new UnionFind(n); + for (var e : edges) { + uf.union(e[0], e[1]); + } + return uf.find(source) == uf.find(destination); + } } 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 index a6d19f52c4fcd..f46da38331785 100644 --- 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 @@ -1,13 +1,31 @@ +class UnionFind: + def __init__(self, n): + self.p = list(range(n)) + self.size = [1] * n + + def find(self, x): + if self.p[x] != x: + self.p[x] = self.find(self.p[x]) + return self.p[x] + + def union(self, a, b): + pa, pb = self.find(a), self.find(b) + if pa == pb: + return False + if self.size[pa] > self.size[pb]: + self.p[pb] = pa + self.size[pa] += self.size[pb] + else: + self.p[pa] = pb + self.size[pb] += self.size[pa] + return True + + 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)) + uf = UnionFind(n) for u, v in edges: - p[find(u)] = find(v) - return find(source) == find(destination) + uf.union(u, v) + return uf.find(source) == uf.find(destination) diff --git a/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.rs b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.rs new file mode 100644 index 0000000000000..8656cd2b90b5f --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution3.rs @@ -0,0 +1,48 @@ +struct UnionFind { + p: Vec, + size: Vec, +} + +impl UnionFind { + fn new(n: usize) -> Self { + let p = (0..n).collect(); + let size = vec![1; n]; + UnionFind { p, size } + } + + fn find(&mut self, x: usize) -> usize { + if self.p[x] != x { + self.p[x] = self.find(self.p[x]); + } + self.p[x] + } + + fn union(&mut self, a: usize, b: usize) { + let pa = self.find(a); + let pb = self.find(b); + if pa != pb { + if self.size[pa] > self.size[pb] { + self.p[pb] = pa; + self.size[pa] += self.size[pb]; + } else { + self.p[pa] = pb; + self.size[pb] += self.size[pa]; + } + } + } +} + +impl Solution { + pub fn valid_path(n: i32, edges: Vec>, source: i32, destination: i32) -> bool { + let n = n as usize; + let mut uf = UnionFind::new(n); + + for e in edges { + let u = e[0] as usize; + let v = e[1] as usize; + uf.union(u, v); + } + + uf.find(source as usize) == uf.find(destination as usize) + } +} 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 index 76c3c8e38adc6..1b99f50f1b18f 100644 --- 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 @@ -1,13 +1,38 @@ -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]); +class UnionFind { + p: number[]; + size: number[]; + constructor(n: number) { + this.p = Array(n) + .fill(0) + .map((_, i) => i); + this.size = Array(n).fill(1); + } + + find(x: number): number { + if (this.p[x] !== x) { + this.p[x] = this.find(this.p[x]); } - return p[x]; - }; - for (const [a, b] of edges) { - p[find(a)] = find(b); + return this.p[x]; } - return find(source) === find(destination); + + union(a: number, b: number): boolean { + const [pa, pb] = [this.find(a), this.find(b)]; + if (pa === pb) { + return false; + } + if (this.size[pa] > this.size[pb]) { + this.p[pb] = pa; + this.size[pa] += this.size[pb]; + } else { + this.p[pa] = pb; + this.size[pb] += this.size[pa]; + } + return true; + } +} + +function validPath(n: number, edges: number[][], source: number, destination: number): boolean { + const uf = new UnionFind(n); + edges.forEach(([u, v]) => uf.union(u, v)); + return uf.find(source) === uf.find(destination); }