diff --git a/solution/0200-0299/0261.Graph Valid Tree/README.md b/solution/0200-0299/0261.Graph Valid Tree/README.md index f6e499f86c501..dfdf7460a3c0f 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/README.md +++ b/solution/0200-0299/0261.Graph Valid Tree/README.md @@ -45,23 +45,33 @@ ## 解法 -### 方法一 +### 方法一:并查集 + +判断是否是树,需要满足以下两个条件: + +1. 边的数量等于节点数减一; +2. 不存在环。 + +我们可以使用并查集来判断是否存在环。遍历边,如果两个节点已经在同一个集合中,说明存在环。否则,我们将两个节点合并到同一个集合中。然后将连通分量的数量减一,最后判断连通分量的数量是否为 $1$。 + +时间复杂度 $O(n \times \log n)$,空间复杂度 $O(n)$。其中 $n$ 是节点数。 ```python class Solution: def validTree(self, n: int, edges: List[List[int]]) -> bool: - def find(x): + def find(x: int) -> int: if p[x] != x: p[x] = find(p[x]) return p[x] p = list(range(n)) for a, b in edges: - if find(a) == find(b): + pa, pb = find(a), find(b) + if pa == pb: return False - p[find(a)] = find(b) + p[pa] = pb n -= 1 return n == 1 ``` @@ -75,12 +85,12 @@ class Solution { for (int i = 0; i < n; ++i) { p[i] = i; } - for (int[] e : edges) { - int a = e[0], b = e[1]; - if (find(a) == find(b)) { + for (var e : edges) { + int pa = find(e[0]), pb = find(e[1]); + if (pa == pb) { return false; } - p[find(a)] = find(b); + p[pa] = pb; --n; } return n == 1; @@ -98,24 +108,25 @@ class Solution { ```cpp class Solution { public: - vector p; - bool validTree(int n, vector>& edges) { - p.resize(n); - for (int i = 0; i < n; ++i) p[i] = i; + vector p(n); + iota(p.begin(), p.end(), 0); + function find = [&](int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + }; for (auto& e : edges) { - int a = e[0], b = e[1]; - if (find(a) == find(b)) return 0; - p[find(a)] = find(b); + int pa = find(e[0]), pb = find(e[1]); + if (pa == pb) { + return false; + } + p[pa] = pb; --n; } return n == 1; } - - int find(int x) { - if (p[x] != x) p[x] = find(p[x]); - return p[x]; - } }; ``` @@ -125,7 +136,7 @@ func validTree(n int, edges [][]int) bool { for i := range p { p[i] = i } - var find func(x int) int + var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) @@ -133,11 +144,11 @@ func validTree(n int, edges [][]int) bool { return p[x] } for _, e := range edges { - a, b := e[0], e[1] - if find(a) == find(b) { + pa, pb := find(e[0]), find(e[1]) + if pa == pb { return false } - p[find(a)] = find(b) + p[pa] = pb n-- } return n == 1 @@ -151,24 +162,170 @@ func validTree(n int, edges [][]int) bool { * @return {boolean} */ var validTree = function (n, edges) { - let p = new Array(n); - for (let i = 0; i < n; ++i) { - p[i] = i; - } - function find(x) { - if (p[x] != x) { + const p = Array.from({ length: n }, (_, i) => i); + const find = x => { + if (p[x] !== x) { p[x] = find(p[x]); } return p[x]; - } + }; for (const [a, b] of edges) { - if (find(a) == find(b)) { + const pa = find(a); + const pb = find(b); + if (pa === pb) { return false; } - p[find(a)] = find(b); + p[pa] = pb; --n; } - return n == 1; + return n === 1; +}; +``` + + + +### 方法二:DFS + +我们也可以使用深度优先搜索来判断是否存在环。我们可以使用一个数组 $vis$ 来记录访问过的节点,搜索时,我们先将节点标记为已访问,然后遍历与该节点相邻的节点,如果相邻节点已经访问过,则跳过,否则递归访问相邻节点。最后,我们判断是否所有节点都被访问过,如果有未访问过的节点,说明无法构成树,返回 `false`。 + +时间复杂度 $O(n)$,空间复杂度 $O(n)$。其中 $n$ 是节点数。 + + + +```python +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + def dfs(i: int): + vis.add(i) + for j in g[i]: + if j not in vis: + dfs(j) + + if len(edges) != n - 1: + return False + g = [[] for _ in range(n)] + for a, b in edges: + g[a].append(b) + g[b].append(a) + vis = set() + dfs(0) + return len(vis) == n +``` + +```java +class Solution { + private List[] g; + private Set vis = new HashSet<>(); + + public boolean validTree(int n, int[][] edges) { + if (edges.length != n - 1) { + return false; + } + 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); + } + dfs(0); + return vis.size() == n; + } + + private void dfs(int i) { + vis.add(i); + for (int j : g[i]) { + if (!vis.contains(j)) { + dfs(j); + } + } + } +} +``` + +```cpp +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() != n - 1) { + return false; + } + vector g[n]; + vector vis(n); + function dfs = [&](int i) { + vis[i] = true; + --n; + for (int j : g[i]) { + if (!vis[j]) { + dfs(j); + } + } + }; + for (auto& e : edges) { + int a = e[0], b = e[1]; + g[a].push_back(b); + g[b].push_back(a); + } + dfs(0); + return n == 0; + } +}; +``` + +```go +func validTree(n int, edges [][]int) bool { + if len(edges) != n-1 { + return false + } + g := make([][]int, n) + vis := make([]bool, n) + for _, e := range edges { + a, b := e[0], e[1] + g[a] = append(g[a], b) + g[b] = append(g[b], a) + } + var dfs func(int) + dfs = func(i int) { + vis[i] = true + n-- + for _, j := range g[i] { + if !vis[j] { + dfs(j) + } + } + } + dfs(0) + return n == 0 +} +``` + +```js +/** + * @param {number} n + * @param {number[][]} edges + * @return {boolean} + */ +var validTree = function (n, edges) { + if (edges.length !== n - 1) { + return false; + } + const g = Array.from({ length: n }, () => []); + const vis = Array.from({ length: n }, () => false); + for (const [a, b] of edges) { + g[a].push(b); + g[b].push(a); + } + const dfs = i => { + vis[i] = true; + --n; + for (const j of g[i]) { + if (!vis[j]) { + dfs(j); + } + } + }; + dfs(0); + return n === 0; }; ``` diff --git a/solution/0200-0299/0261.Graph Valid Tree/README_EN.md b/solution/0200-0299/0261.Graph Valid Tree/README_EN.md index b20da76ff325b..98baff413198c 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/README_EN.md +++ b/solution/0200-0299/0261.Graph Valid Tree/README_EN.md @@ -39,54 +39,60 @@ ## Solutions -### Solution 1 +### Solution 1: Union-Find + +To determine whether it is a tree, the following two conditions must be met: + +1. The number of edges is equal to the number of nodes minus one; +2. There is no cycle. + +We can use a union-find set to determine whether there is a cycle. We traverse the edges, if two nodes are already in the same set, it means there is a cycle. Otherwise, we merge the two nodes into the same set. Then we decrease the number of connected components by one, and finally check whether the number of connected components is $1$. + +The time complexity is $O(n \times \log n)$, and the space complexity is $O(n)$, where $n$ is the number of nodes. ```python class Solution: def validTree(self, n: int, edges: List[List[int]]) -> bool: - def find(x): + def find(x: int) -> int: if p[x] != x: p[x] = find(p[x]) return p[x] p = list(range(n)) for a, b in edges: - if find(a) == find(b): + pa, pb = find(a), find(b) + if pa == pb: return False - p[find(a)] = find(b) + p[pa] = pb n -= 1 return n == 1 ``` ```java class Solution { - private int[] p; - - public boolean validTree(int n, int[][] edges) { - p = new int[n]; - for (int i = 0; i < n; ++i) { - p[i] = i; - } - for (int[] e : edges) { - int a = e[0], b = e[1]; - if (find(a) == find(b)) { +public: + bool validTree(int n, vector>& edges) { + vector p(n); + iota(p.begin(), p.end(), 0); + function find = [&](int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + }; + for (auto& e : edges) { + int pa = find(e[0]), pb = find(e[1]); + if (pa == pb) { return false; } - p[find(a)] = find(b); + p[pa] = pb; --n; } return n == 1; } - - private int find(int x) { - if (p[x] != x) { - p[x] = find(p[x]); - } - return p[x]; - } -} +}; ``` ```cpp @@ -119,7 +125,7 @@ func validTree(n int, edges [][]int) bool { for i := range p { p[i] = i } - var find func(x int) int + var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) @@ -127,11 +133,11 @@ func validTree(n int, edges [][]int) bool { return p[x] } for _, e := range edges { - a, b := e[0], e[1] - if find(a) == find(b) { + pa, pb := find(e[0]), find(e[1]) + if pa == pb { return false } - p[find(a)] = find(b) + p[pa] = pb n-- } return n == 1 @@ -145,27 +151,175 @@ func validTree(n int, edges [][]int) bool { * @return {boolean} */ var validTree = function (n, edges) { - let p = new Array(n); - for (let i = 0; i < n; ++i) { - p[i] = i; - } - function find(x) { - if (p[x] != x) { + const p = Array.from({ length: n }, (_, i) => i); + const find = x => { + if (p[x] !== x) { p[x] = find(p[x]); } return p[x]; - } + }; for (const [a, b] of edges) { - if (find(a) == find(b)) { + const pa = find(a); + const pb = find(b); + if (pa === pb) { return false; } - p[find(a)] = find(b); + p[pa] = pb; --n; } - return n == 1; + return n === 1; +}; +``` + + + +### Solution 2: DFS + +We can also use depth-first search to determine whether there is a cycle. We can use an array $vis$ to record the visited nodes. During the search, we first mark the node as visited, then traverse the nodes adjacent to this node. If the adjacent node has been visited, we skip it, otherwise we recursively visit the adjacent node. Finally, we check whether all nodes have been visited. If there are nodes that have not been visited, it means that it cannot form a tree, so we return `false`. + +The time complexity is $O(n)$, and the space complexity is $O(n)$, where $n$ is the number of nodes. + + + +```python +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + def dfs(i: int): + vis.add(i) + for j in g[i]: + if j not in vis: + dfs(j) + + if len(edges) != n - 1: + return False + g = [[] for _ in range(n)] + for a, b in edges: + g[a].append(b) + g[b].append(a) + vis = set() + dfs(0) + return len(vis) == n +``` + +```java +class Solution { + private List[] g; + private Set vis = new HashSet<>(); + + public boolean validTree(int n, int[][] edges) { + if (edges.length != n - 1) { + return false; + } + 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); + } + dfs(0); + return vis.size() == n; + } + + private void dfs(int i) { + vis.add(i); + for (int j : g[i]) { + if (!vis.contains(j)) { + dfs(j); + } + } + } +} +``` + +```cpp +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() != n - 1) { + return false; + } + vector g[n]; + vector vis(n); + function dfs = [&](int i) { + vis[i] = true; + --n; + for (int j : g[i]) { + if (!vis[j]) { + dfs(j); + } + } + }; + for (auto& e : edges) { + int a = e[0], b = e[1]; + g[a].push_back(b); + g[b].push_back(a); + } + dfs(0); + return n == 0; + } +}; +``` + +```go +func validTree(n int, edges [][]int) bool { + if len(edges) != n-1 { + return false + } + g := make([][]int, n) + vis := make([]bool, n) + for _, e := range edges { + a, b := e[0], e[1] + g[a] = append(g[a], b) + g[b] = append(g[b], a) + } + var dfs func(int) + dfs = func(i int) { + vis[i] = true + n-- + for _, j := range g[i] { + if !vis[j] { + dfs(j) + } + } + } + dfs(0) + return n == 0 +} +``` + +```js +/** + * @param {number} n + * @param {number[][]} edges + * @return {boolean} + */ +var validTree = function (n, edges) { + if (edges.length !== n - 1) { + return false; + } + const g = Array.from({ length: n }, () => []); + const vis = Array.from({ length: n }, () => false); + for (const [a, b] of edges) { + g[a].push(b); + g[b].push(a); + } + const dfs = i => { + vis[i] = true; + --n; + for (const j of g[i]) { + if (!vis[j]) { + dfs(j); + } + } + }; + dfs(0); + return n === 0; }; ``` + + diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution.cpp b/solution/0200-0299/0261.Graph Valid Tree/Solution.cpp index a04f5d7e0eef4..9ebb82b274a18 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/Solution.cpp +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution.cpp @@ -1,21 +1,22 @@ class Solution { public: - vector p; - bool validTree(int n, vector>& edges) { - p.resize(n); - for (int i = 0; i < n; ++i) p[i] = i; + vector p(n); + iota(p.begin(), p.end(), 0); + function find = [&](int x) { + if (p[x] != x) { + p[x] = find(p[x]); + } + return p[x]; + }; for (auto& e : edges) { - int a = e[0], b = e[1]; - if (find(a) == find(b)) return 0; - p[find(a)] = find(b); + int pa = find(e[0]), pb = find(e[1]); + if (pa == pb) { + return false; + } + p[pa] = pb; --n; } return n == 1; } - - 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/0200-0299/0261.Graph Valid Tree/Solution.go b/solution/0200-0299/0261.Graph Valid Tree/Solution.go index e3dc83d490b56..797a68591ec4e 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/Solution.go +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution.go @@ -3,7 +3,7 @@ func validTree(n int, edges [][]int) bool { for i := range p { p[i] = i } - var find func(x int) int + var find func(int) int find = func(x int) int { if p[x] != x { p[x] = find(p[x]) @@ -11,11 +11,11 @@ func validTree(n int, edges [][]int) bool { return p[x] } for _, e := range edges { - a, b := e[0], e[1] - if find(a) == find(b) { + pa, pb := find(e[0]), find(e[1]) + if pa == pb { return false } - p[find(a)] = find(b) + p[pa] = pb n-- } return n == 1 diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution.java b/solution/0200-0299/0261.Graph Valid Tree/Solution.java index 6b649c5556cbc..0d069fa607581 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/Solution.java +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution.java @@ -6,12 +6,12 @@ public boolean validTree(int n, int[][] edges) { for (int i = 0; i < n; ++i) { p[i] = i; } - for (int[] e : edges) { - int a = e[0], b = e[1]; - if (find(a) == find(b)) { + for (var e : edges) { + int pa = find(e[0]), pb = find(e[1]); + if (pa == pb) { return false; } - p[find(a)] = find(b); + p[pa] = pb; --n; } return n == 1; diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution.js b/solution/0200-0299/0261.Graph Valid Tree/Solution.js index d3462065735df..c62d62a613c9f 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/Solution.js +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution.js @@ -4,22 +4,21 @@ * @return {boolean} */ var validTree = function (n, edges) { - let p = new Array(n); - for (let i = 0; i < n; ++i) { - p[i] = i; - } - function find(x) { - if (p[x] != x) { + const p = Array.from({ length: n }, (_, i) => i); + const find = x => { + if (p[x] !== x) { p[x] = find(p[x]); } return p[x]; - } + }; for (const [a, b] of edges) { - if (find(a) == find(b)) { + const pa = find(a); + const pb = find(b); + if (pa === pb) { return false; } - p[find(a)] = find(b); + p[pa] = pb; --n; } - return n == 1; + return n === 1; }; diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution.py b/solution/0200-0299/0261.Graph Valid Tree/Solution.py index 5806e3e8d34ba..426a340337099 100644 --- a/solution/0200-0299/0261.Graph Valid Tree/Solution.py +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution.py @@ -1,14 +1,15 @@ class Solution: def validTree(self, n: int, edges: List[List[int]]) -> bool: - def find(x): + def find(x: int) -> int: if p[x] != x: p[x] = find(p[x]) return p[x] p = list(range(n)) for a, b in edges: - if find(a) == find(b): + pa, pb = find(a), find(b) + if pa == pb: return False - p[find(a)] = find(b) + p[pa] = pb n -= 1 return n == 1 diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution2.cpp b/solution/0200-0299/0261.Graph Valid Tree/Solution2.cpp new file mode 100644 index 0000000000000..fab2975fb7e6c --- /dev/null +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution2.cpp @@ -0,0 +1,26 @@ +class Solution { +public: + bool validTree(int n, vector>& edges) { + if (edges.size() != n - 1) { + return false; + } + vector g[n]; + vector vis(n); + function dfs = [&](int i) { + vis[i] = true; + --n; + for (int j : g[i]) { + if (!vis[j]) { + dfs(j); + } + } + }; + for (auto& e : edges) { + int a = e[0], b = e[1]; + g[a].push_back(b); + g[b].push_back(a); + } + dfs(0); + return n == 0; + } +}; \ No newline at end of file diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution2.go b/solution/0200-0299/0261.Graph Valid Tree/Solution2.go new file mode 100644 index 0000000000000..df8250a04f704 --- /dev/null +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution2.go @@ -0,0 +1,24 @@ +func validTree(n int, edges [][]int) bool { + if len(edges) != n-1 { + return false + } + g := make([][]int, n) + vis := make([]bool, n) + for _, e := range edges { + a, b := e[0], e[1] + g[a] = append(g[a], b) + g[b] = append(g[b], a) + } + var dfs func(int) + dfs = func(i int) { + vis[i] = true + n-- + for _, j := range g[i] { + if !vis[j] { + dfs(j) + } + } + } + dfs(0) + return n == 0 +} \ No newline at end of file diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution2.java b/solution/0200-0299/0261.Graph Valid Tree/Solution2.java new file mode 100644 index 0000000000000..e56f3bf18a6aa --- /dev/null +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution2.java @@ -0,0 +1,28 @@ +class Solution { + private List[] g; + private Set vis = new HashSet<>(); + + public boolean validTree(int n, int[][] edges) { + if (edges.length != n - 1) { + return false; + } + 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); + } + dfs(0); + return vis.size() == n; + } + + private void dfs(int i) { + vis.add(i); + for (int j : g[i]) { + if (!vis.contains(j)) { + dfs(j); + } + } + } +} \ No newline at end of file diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution2.js b/solution/0200-0299/0261.Graph Valid Tree/Solution2.js new file mode 100644 index 0000000000000..66a9f37baf6c9 --- /dev/null +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution2.js @@ -0,0 +1,27 @@ +/** + * @param {number} n + * @param {number[][]} edges + * @return {boolean} + */ +var validTree = function (n, edges) { + if (edges.length !== n - 1) { + return false; + } + const g = Array.from({ length: n }, () => []); + const vis = Array.from({ length: n }, () => false); + for (const [a, b] of edges) { + g[a].push(b); + g[b].push(a); + } + const dfs = i => { + vis[i] = true; + --n; + for (const j of g[i]) { + if (!vis[j]) { + dfs(j); + } + } + }; + dfs(0); + return n === 0; +}; diff --git a/solution/0200-0299/0261.Graph Valid Tree/Solution2.py b/solution/0200-0299/0261.Graph Valid Tree/Solution2.py new file mode 100644 index 0000000000000..1d9a16b367c54 --- /dev/null +++ b/solution/0200-0299/0261.Graph Valid Tree/Solution2.py @@ -0,0 +1,17 @@ +class Solution: + def validTree(self, n: int, edges: List[List[int]]) -> bool: + def dfs(i: int): + vis.add(i) + for j in g[i]: + if j not in vis: + dfs(j) + + if len(edges) != n - 1: + return False + g = [[] for _ in range(n)] + for a, b in edges: + g[a].append(b) + g[b].append(a) + vis = set() + dfs(0) + return len(vis) == n