Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 475b185

Browse files
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)

AltStyle によって変換されたページ (->オリジナル) /