Skip to content

Commit 475b185

Browse files
authored
feat: add solutions to lc problem: No.261 (doocs#2603)
No.0261.Graph Valid Tree
1 parent 784fa25 commit 475b185

File tree

12 files changed

+538
-104
lines changed

12 files changed

+538
-104
lines changed

solution/0200-0299/0261.Graph Valid Tree/README.md

Lines changed: 191 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -45,23 +45,33 @@
4545

4646
## 解法
4747

48-
### 方法一
48+
### 方法一:并查集
49+
50+
判断是否是树,需要满足以下两个条件:
51+
52+
1. 边的数量等于节点数减一;
53+
2. 不存在环。
54+
55+
我们可以使用并查集来判断是否存在环。遍历边,如果两个节点已经在同一个集合中,说明存在环。否则,我们将两个节点合并到同一个集合中。然后将连通分量的数量减一,最后判断连通分量的数量是否为 $1$。
56+
57+
时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 是节点数。
4958

5059
<!-- tabs:start -->
5160

5261
```python
5362
class Solution:
5463
def validTree(self, n: int, edges: List[List[int]]) -> bool:
55-
def find(x):
64+
def find(x: int) -> int:
5665
if p[x] != x:
5766
p[x] = find(p[x])
5867
return p[x]
5968

6069
p = list(range(n))
6170
for a, b in edges:
62-
if find(a) == find(b):
71+
pa, pb = find(a), find(b)
72+
if pa == pb:
6373
return False
64-
p[find(a)] = find(b)
74+
p[pa] = pb
6575
n -= 1
6676
return n == 1
6777
```
@@ -75,12 +85,12 @@ class Solution {
7585
for (int i = 0; i < n; ++i) {
7686
p[i] = i;
7787
}
78-
for (int[] e : edges) {
79-
int a = e[0], b = e[1];
80-
if (find(a) == find(b)) {
88+
for (var e : edges) {
89+
int pa = find(e[0]), pb = find(e[1]);
90+
if (pa == pb) {
8191
return false;
8292
}
83-
p[find(a)] = find(b);
93+
p[pa] = pb;
8494
--n;
8595
}
8696
return n == 1;
@@ -98,24 +108,25 @@ class Solution {
98108
```cpp
99109
class Solution {
100110
public:
101-
vector<int> p;
102-
103111
bool validTree(int n, vector<vector<int>>& edges) {
104-
p.resize(n);
105-
for (int i = 0; i < n; ++i) p[i] = i;
112+
vector<int> p(n);
113+
iota(p.begin(), p.end(), 0);
114+
function<int(int)> find = [&](int x) {
115+
if (p[x] != x) {
116+
p[x] = find(p[x]);
117+
}
118+
return p[x];
119+
};
106120
for (auto& e : edges) {
107-
int a = e[0], b = e[1];
108-
if (find(a) == find(b)) return 0;
109-
p[find(a)] = find(b);
121+
int pa = find(e[0]), pb = find(e[1]);
122+
if (pa == pb) {
123+
return false;
124+
}
125+
p[pa] = pb;
110126
--n;
111127
}
112128
return n == 1;
113129
}
114-
115-
int find(int x) {
116-
if (p[x] != x) p[x] = find(p[x]);
117-
return p[x];
118-
}
119130
};
120131
```
121132
@@ -125,19 +136,19 @@ func validTree(n int, edges [][]int) bool {
125136
for i := range p {
126137
p[i] = i
127138
}
128-
var find func(x int) int
139+
var find func(int) int
129140
find = func(x int) int {
130141
if p[x] != x {
131142
p[x] = find(p[x])
132143
}
133144
return p[x]
134145
}
135146
for _, e := range edges {
136-
a, b := e[0], e[1]
137-
if find(a) == find(b) {
147+
pa, pb := find(e[0]), find(e[1])
148+
if pa == pb {
138149
return false
139150
}
140-
p[find(a)] = find(b)
151+
p[pa] = pb
141152
n--
142153
}
143154
return n == 1
@@ -151,24 +162,170 @@ func validTree(n int, edges [][]int) bool {
151162
* @return {boolean}
152163
*/
153164
var validTree = function (n, edges) {
154-
let p = new Array(n);
155-
for (let i = 0; i < n; ++i) {
156-
p[i] = i;
157-
}
158-
function find(x) {
159-
if (p[x] != x) {
165+
const p = Array.from({ length: n }, (_, i) => i);
166+
const find = x => {
167+
if (p[x] !== x) {
160168
p[x] = find(p[x]);
161169
}
162170
return p[x];
163-
}
171+
};
164172
for (const [a, b] of edges) {
165-
if (find(a) == find(b)) {
173+
const pa = find(a);
174+
const pb = find(b);
175+
if (pa === pb) {
166176
return false;
167177
}
168-
p[find(a)] = find(b);
178+
p[pa] = pb;
169179
--n;
170180
}
171-
return n == 1;
181+
return n === 1;
182+
};
183+
```
184+
185+
<!-- tabs:end -->
186+
187+
### 方法二:DFS
188+
189+
我们也可以使用深度优先搜索来判断是否存在环。我们可以使用一个数组 $vis$ 来记录访问过的节点,搜索时,我们先将节点标记为已访问,然后遍历与该节点相邻的节点,如果相邻节点已经访问过,则跳过,否则递归访问相邻节点。最后,我们判断是否所有节点都被访问过,如果有未访问过的节点,说明无法构成树,返回 `false`
190+
191+
时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是节点数。
192+
193+
<!-- tabs:start -->
194+
195+
```python
196+
class Solution:
197+
def validTree(self, n: int, edges: List[List[int]]) -> bool:
198+
def dfs(i: int):
199+
vis.add(i)
200+
for j in g[i]:
201+
if j not in vis:
202+
dfs(j)
203+
204+
if len(edges) != n - 1:
205+
return False
206+
g = [[] for _ in range(n)]
207+
for a, b in edges:
208+
g[a].append(b)
209+
g[b].append(a)
210+
vis = set()
211+
dfs(0)
212+
return len(vis) == n
213+
```
214+
215+
```java
216+
class Solution {
217+
private List<Integer>[] g;
218+
private Set<Integer> vis = new HashSet<>();
219+
220+
public boolean validTree(int n, int[][] edges) {
221+
if (edges.length != n - 1) {
222+
return false;
223+
}
224+
g = new List[n];
225+
Arrays.setAll(g, k -> new ArrayList<>());
226+
for (var e : edges) {
227+
int a = e[0], b = e[1];
228+
g[a].add(b);
229+
g[b].add(a);
230+
}
231+
dfs(0);
232+
return vis.size() == n;
233+
}
234+
235+
private void dfs(int i) {
236+
vis.add(i);
237+
for (int j : g[i]) {
238+
if (!vis.contains(j)) {
239+
dfs(j);
240+
}
241+
}
242+
}
243+
}
244+
```
245+
246+
```cpp
247+
class Solution {
248+
public:
249+
bool validTree(int n, vector<vector<int>>& edges) {
250+
if (edges.size() != n - 1) {
251+
return false;
252+
}
253+
vector<int> g[n];
254+
vector<int> vis(n);
255+
function<void(int)> dfs = [&](int i) {
256+
vis[i] = true;
257+
--n;
258+
for (int j : g[i]) {
259+
if (!vis[j]) {
260+
dfs(j);
261+
}
262+
}
263+
};
264+
for (auto& e : edges) {
265+
int a = e[0], b = e[1];
266+
g[a].push_back(b);
267+
g[b].push_back(a);
268+
}
269+
dfs(0);
270+
return n == 0;
271+
}
272+
};
273+
```
274+
275+
```go
276+
func validTree(n int, edges [][]int) bool {
277+
if len(edges) != n-1 {
278+
return false
279+
}
280+
g := make([][]int, n)
281+
vis := make([]bool, n)
282+
for _, e := range edges {
283+
a, b := e[0], e[1]
284+
g[a] = append(g[a], b)
285+
g[b] = append(g[b], a)
286+
}
287+
var dfs func(int)
288+
dfs = func(i int) {
289+
vis[i] = true
290+
n--
291+
for _, j := range g[i] {
292+
if !vis[j] {
293+
dfs(j)
294+
}
295+
}
296+
}
297+
dfs(0)
298+
return n == 0
299+
}
300+
```
301+
302+
```js
303+
/**
304+
* @param {number} n
305+
* @param {number[][]} edges
306+
* @return {boolean}
307+
*/
308+
var validTree = function (n, edges) {
309+
if (edges.length !== n - 1) {
310+
return false;
311+
}
312+
const g = Array.from({ length: n }, () => []);
313+
const vis = Array.from({ length: n }, () => false);
314+
for (const [a, b] of edges) {
315+
g[a].push(b);
316+
g[b].push(a);
317+
}
318+
const dfs = i => {
319+
vis[i] = true;
320+
--n;
321+
for (const j of g[i]) {
322+
if (!vis[j]) {
323+
dfs(j);
324+
}
325+
}
326+
};
327+
dfs(0);
328+
return n === 0;
172329
};
173330
```
174331

0 commit comments

Comments
 (0)