53
53
54
54
** 方法一:DFS**
55
55
56
+ 我们先将 ` edges ` 转换成邻接表 $g$,然后使用 DFS,判断是否存在从 ` source ` 到 ` destination ` 的路径。
57
+
58
+ 过程中,我们用数组 ` vis ` 记录已经访问过的顶点,避免重复访问。
59
+
60
+ 时间复杂度 $O(n + m)$,其中 $n$ 和 $m$ 分别是节点数和边数。
61
+
56
62
** 方法二:并查集**
57
63
64
+ 判断图中两个节点是否连通,一种比较简单直接的方法是使用并查集。
65
+
66
+ 先构建并查集,然后将每条边的两个节点合并。
67
+
68
+ 最后查询 ` source ` 和 ` destination ` 的祖宗节点是否相同,相同则说明两个节点连通。
69
+
70
+ 时间复杂度 $O(n + m \times \alpha(m))$,空间复杂度 $O(n)$。其中 $n$ 和 $m$ 分别是节点数和边数。
71
+
72
+ 附并查集相关介绍以及常用模板:
73
+
74
+ 并查集是一种树形的数据结构,顾名思义,它用于处理一些不交集的** 合并** 及** 查询** 问题。 它支持两种操作:
75
+
76
+ 1 . 查找(Find):确定某个元素处于哪个子集,单次操作时间复杂度 $O(\alpha(n))$
77
+ 1 . 合并(Union):将两个子集合并成一个集合,单次操作时间复杂度 $O(\alpha(n))$
78
+
79
+ 其中 $\alpha$ 为阿克曼函数的反函数,其增长极其缓慢,也就是说其单次操作的平均运行时间可以认为是一个很小的常数。
80
+
81
+ 以下是并查集的常用模板,需要熟练掌握。其中:
82
+
83
+ - ` n ` 表示节点数
84
+ - ` p ` 存储每个点的父节点,初始时每个点的父节点都是自己
85
+ - ` size ` 只有当节点是祖宗节点时才有意义,表示祖宗节点所在集合中,点的数量
86
+ - ` find(x) ` 函数用于查找 $x$ 所在集合的祖宗节点
87
+ - ` union(a, b) ` 函数用于合并 $a$ 和 $b$ 所在的集合
88
+
89
+ ``` python [sol1-Python3 模板]
90
+ p = list (range (n))
91
+ size = [1 ] * n
92
+
93
+ def find (x ):
94
+ if p[x] != x:
95
+ # 路径压缩
96
+ p[x] = find(p[x])
97
+ return p[x]
98
+
99
+
100
+ def union (a , b ):
101
+ pa, pb = find(a), find(b)
102
+ if pa == pb:
103
+ return
104
+ p[pa] = pb
105
+ size[pb] += size[pa]
106
+ ```
107
+
108
+ ``` java [sol1-Java 模板]
109
+ int [] p = new int [n];
110
+ int [] size = new int [n];
111
+ for (int i = 0 ; i < n; ++ i) {
112
+ p[i] = i;
113
+ size[i] = 1 ;
114
+ }
115
+
116
+ int find(int x) {
117
+ if (p[x] != x) {
118
+ // 路径压缩
119
+ p[x] = find(p[x]);
120
+ }
121
+ return p[x];
122
+ }
123
+
124
+ void union(int a, int b) {
125
+ int pa = find(a), pb = find(b);
126
+ if (pa == pb) {
127
+ return ;
128
+ }
129
+ p[pa] = pb;
130
+ size[pb] += size[pa];
131
+ }
132
+ ```
133
+
134
+ ``` cpp [sol1-C++ 模板]
135
+ vector<int > p (n);
136
+ iota(p.begin(), p.end(), 0);
137
+ vector<int > size(n, 1);
138
+
139
+ int find(int x) {
140
+ if (p[ x] != x) {
141
+ // 路径压缩
142
+ p[ x] = find(p[ x] );
143
+ }
144
+ return p[ x] ;
145
+ }
146
+
147
+ void unite(int a, int b) {
148
+ int pa = find(a), pb = find(b);
149
+ if (pa == pb) return;
150
+ p[ pa] = pb;
151
+ size[ pb] += size[ pa] ;
152
+ }
153
+ ```
154
+
155
+ ```go [sol1-Go 模板]
156
+ p := make([]int, n)
157
+ size := make([]int, n)
158
+ for i := range p {
159
+ p[i] = i
160
+ size[i] = 1
161
+ }
162
+
163
+ func find(x int) int {
164
+ if p[x] != x {
165
+ // 路径压缩
166
+ p[x] = find(p[x])
167
+ }
168
+ return p[x]
169
+ }
170
+
171
+ func union(a, b int) {
172
+ pa, pb := find(a), find(b)
173
+ if pa == pb {
174
+ return
175
+ }
176
+ p[pa] = pb
177
+ size[pb] += size[pa]
178
+ }
179
+ ```
180
+
58
181
<!-- tabs:start -->
59
182
60
183
### ** Python3**
63
186
64
187
``` python
65
188
class Solution :
66
- def validPath (self , n : int , edges : List[List[int ]], start : int , end : int ) -> bool :
67
- def dfs (u ):
68
- nonlocal ans
69
- if ans or u in vis:
70
- return
71
- vis.add(u)
72
- if u == end:
73
- ans = True
74
- return
75
- for v in g[u]:
76
- dfs(v)
189
+ def validPath (self , n : int , edges : List[List[int ]], source : int , destination : int ) -> bool :
190
+ def dfs (i ):
191
+ if i == destination:
192
+ return True
193
+ vis.add(i)
194
+ for j in g[i]:
195
+ if j not in vis and dfs(j):
196
+ return True
197
+ return False
77
198
78
199
g = defaultdict(list )
200
+ for a, b in edges:
201
+ g[a].append(b)
202
+ g[b].append(a)
79
203
vis = set ()
80
- ans = False
81
- for u, v in edges:
82
- g[u].append(v)
83
- g[v].append(u)
84
- dfs(start)
85
- return ans
204
+ return dfs(source)
86
205
```
87
206
88
207
``` python
@@ -103,6 +222,38 @@ class Solution:
103
222
104
223
<!-- 这里可写当前语言的特殊实现逻辑 -->
105
224
225
+ ``` java
226
+ class Solution {
227
+ private boolean [] vis;
228
+ private List<Integer > [] g;
229
+
230
+ public boolean validPath (int n , int [][] edges , int source , int destination ) {
231
+ vis = new boolean [n];
232
+ g = new List [n];
233
+ Arrays . setAll(g, k - > new ArrayList<> ());
234
+ for (var e : edges) {
235
+ int a = e[0 ], b = e[1 ];
236
+ g[a]. add(b);
237
+ g[b]. add(a);
238
+ }
239
+ return dfs(source, destination);
240
+ }
241
+
242
+ private boolean dfs (int source , int destination ) {
243
+ if (source == destination) {
244
+ return true ;
245
+ }
246
+ vis[source] = true ;
247
+ for (int nxt : g[source]) {
248
+ if (! vis[nxt] && dfs(nxt, destination)) {
249
+ return true ;
250
+ }
251
+ }
252
+ return false ;
253
+ }
254
+ }
255
+ ```
256
+
106
257
``` java
107
258
class Solution {
108
259
private int [] p;
@@ -132,24 +283,73 @@ class Solution {
132
283
``` cpp
133
284
class Solution {
134
285
public:
135
- vector<int > p;
286
+ bool validPath(int n, vector<vector<int >>& edges, int source, int destination) {
287
+ vector<bool > vis(n);
288
+ vector<vector<int >> g(n);
289
+ for (auto& e : edges) {
290
+ int a = e[ 0] , b = e[ 1] ;
291
+ g[ a] .emplace_back(b);
292
+ g[ b] .emplace_back(a);
293
+ }
294
+ function<bool(int)> dfs = [ &] (int i) -> bool {
295
+ if (i == destination) return true;
296
+ vis[ i] = true;
297
+ for (int& j : g[ i] ) {
298
+ if (!vis[ j] && dfs(j)) {
299
+ return true;
300
+ }
301
+ }
302
+ return false;
303
+ };
304
+ return dfs(source);
305
+ }
306
+ };
307
+ ```
136
308
309
+ ```cpp
310
+ class Solution {
311
+ public:
137
312
bool validPath(int n, vector<vector<int>>& edges, int source, int destination) {
138
- p.resize(n);
139
- for (int i = 0; i < n; ++i) p[i] = i;
313
+ vector<int> p(n);
314
+ iota(p.begin(), p.end(), 0);
315
+ function<int(int)> find = [&](int x) -> int {
316
+ if (p[x] != x) p[x] = find(p[x]);
317
+ return p[x];
318
+ };
140
319
for (auto& e : edges) p[find(e[0])] = find(e[1]);
141
320
return find(source) == find(destination);
142
321
}
143
-
144
- int find (int x) {
145
- if (p[ x] != x) p[ x] = find(p[ x] );
146
- return p[ x] ;
147
- }
148
322
};
149
323
```
150
324
151
325
### ** Go**
152
326
327
+ ``` go
328
+ func validPath (n int , edges [][]int , source int , destination int ) bool {
329
+ vis := make ([]bool , n)
330
+ g := make ([][]int , n)
331
+ for _ , e := range edges {
332
+ a , b := e[0 ], e[1 ]
333
+ g[a] = append (g[a], b)
334
+ g[b] = append (g[b], a)
335
+ }
336
+ var dfs func (int ) bool
337
+ dfs = func (i int ) bool {
338
+ if i == destination {
339
+ return true
340
+ }
341
+ vis[i] = true
342
+ for _ , j := range g[i] {
343
+ if !vis[j] && dfs (j) {
344
+ return true
345
+ }
346
+ }
347
+ return false
348
+ }
349
+ return dfs (source)
350
+ }
351
+ ```
352
+
153
353
``` go
154
354
func validPath (n int , edges [][]int , source int , destination int ) bool {
155
355
p := make ([]int , n)
0 commit comments