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); +} +``` + 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..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,35 +192,272 @@ 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`. + +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. + +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; + } +} +``` + +#### 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); + } + } + } + return false; + } +}; +``` + +#### Go - let p_s = Solution::find(source, &mut disjoint_set); - let p_d = Solution::find(destination, &mut disjoint_set); +```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 +} +``` - p_s == p_d +#### 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); } - 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); + const vis = new Set(); + const q = [source]; + + while (q.length) { + const i = q.pop()!; + if (i === destination) { + return true; + } + if (vis.has(i)) { + continue; } - d_set[x as usize] + vis.add(i); + q.push(...g[i]); + } + + 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 } } ``` @@ -221,7 +468,16 @@ impl Solution { -### 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. @@ -278,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); } }; @@ -309,6 +569,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); +} +``` + 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 +}; 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 +} 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) 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..649dc030166ae --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution.ts @@ -0,0 +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); + } + + 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); +} 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 +}; 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 +} 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 +} 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 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 + } +} 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..1d741f6bb04f2 --- /dev/null +++ b/solution/1900-1999/1971.Find if Path Exists in Graph/Solution2.ts @@ -0,0 +1,25 @@ +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 q = [source]; + + 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 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 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); + } +}; 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) +} 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]; + } +} 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) 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); +}