LeetCode 261: Graph Valid Tree (Union-Find Connectivity Check)
LeetCode 261GraphUnion-FindToday we solve LeetCode 261 - Graph Valid Tree.
Source: https://leetcode.com/problems/graph-valid-tree/
English
Problem Summary
Given n nodes and an undirected edge list, determine whether the graph forms a valid tree.
Key Insight
A valid tree must satisfy both conditions: exactly n - 1 edges and no cycle (equivalently, all nodes become one connected component).
Algorithm
- If edge count is not n - 1, return false immediately.
- Use Union-Find; for each edge (u, v), if find(u) == find(v), cycle exists.
- Otherwise union them.
- If all edges processed without cycle, return true.
Complexity Analysis
Time: O(n + m * α(n)), with path compression and union by rank.
Space: O(n).
Reference Implementations (Java / Go / C++ / Python / JavaScript)
class Solution {
public boolean validTree(int n, int[][] edges) {
if (edges.length != n - 1) return false;
int[] parent = new int[n];
int[] rank = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
for (int[] e : edges) {
int ru = find(parent, e[0]);
int rv = find(parent, e[1]);
if (ru == rv) return false;
if (rank[ru] < rank[rv]) parent[ru] = rv;
else if (rank[ru] > rank[rv]) parent[rv] = ru;
else {
parent[rv] = ru;
rank[ru]++;
}
}
return true;
}
private int find(int[] parent, int x) {
if (parent[x] != x) parent[x] = find(parent, parent[x]);
return parent[x];
}
}func validTree(n int, edges [][]int) bool {
if len(edges) != n-1 {
return false
}
parent := make([]int, n)
rank := make([]int, n)
for i := 0; i < n; i++ {
parent[i] = i
}
var find func(int) int
find = func(x int) int {
if parent[x] != x {
parent[x] = find(parent[x])
}
return parent[x]
}
for _, e := range edges {
ru, rv := find(e[0]), find(e[1])
if ru == rv {
return false
}
if rank[ru] < rank[rv] {
parent[ru] = rv
} else if rank[ru] > rank[rv] {
parent[rv] = ru
} else {
parent[rv] = ru
rank[ru]++
}
}
return true
}class Solution {
public:
bool validTree(int n, vector<vector<int>>& edges) {
if ((int)edges.size() != n - 1) return false;
vector<int> parent(n), rank(n, 0);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
};
for (auto& e : edges) {
int ru = find(e[0]), rv = find(e[1]);
if (ru == rv) return false;
if (rank[ru] < rank[rv]) parent[ru] = rv;
else if (rank[ru] > rank[rv]) parent[rv] = ru;
else {
parent[rv] = ru;
rank[ru]++;
}
}
return true;
}
};class Solution:
def validTree(self, n: int, edges: list[list[int]]) -> bool:
if len(edges) != n - 1:
return False
parent = list(range(n))
rank = [0] * n
def find(x: int) -> int:
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
for u, v in edges:
ru, rv = find(u), find(v)
if ru == rv:
return False
if rank[ru] < rank[rv]:
parent[ru] = rv
elif rank[ru] > rank[rv]:
parent[rv] = ru
else:
parent[rv] = ru
rank[ru] += 1
return Truefunction validTree(n, edges) {
if (edges.length !== n - 1) return false;
const parent = Array.from({ length: n }, (_, i) => i);
const rank = Array(n).fill(0);
const find = (x) => {
if (parent[x] !== x) parent[x] = find(parent[x]);
return parent[x];
};
for (const [u, v] of edges) {
const ru = find(u), rv = find(v);
if (ru === rv) return false;
if (rank[ru] < rank[rv]) parent[ru] = rv;
else if (rank[ru] > rank[rv]) parent[rv] = ru;
else {
parent[rv] = ru;
rank[ru]++;
}
}
return true;
}中文
题目概述
给定 n 个节点和无向边列表,判断该图是否是一棵合法树。
核心思路
合法树必须同时满足两点:边数恰好为 n - 1,并且图中不存在环(等价于最终连成一个连通分量)。
算法步骤
- 若边数不是 n - 1,直接返回 false。
- 用并查集处理每条边 (u, v),若两端根相同说明出现环。
- 否则执行 union 合并。
- 全部边处理完且无环,即可返回 true。
复杂度分析
时间复杂度:O(n + m * α(n))(路径压缩 + 按秩合并)。
空间复杂度:O(n)。
多语言参考实现(Java / Go / C++ / Python / JavaScript)
class Solution {
public boolean validTree(int n, int[][] edges) {
if (edges.length != n - 1) return false;
int[] parent = new int[n];
int[] rank = new int[n];
for (int i = 0; i < n; i++) parent[i] = i;
for (int[] e : edges) {
int ru = find(parent, e[0]);
int rv = find(parent, e[1]);
if (ru == rv) return false;
if (rank[ru] < rank[rv]) parent[ru] = rv;
else if (rank[ru] > rank[rv]) parent[rv] = ru;
else {
parent[rv] = ru;
rank[ru]++;
}
}
return true;
}
private int find(int[] parent, int x) {
if (parent[x] != x) parent[x] = find(parent, parent[x]);
return parent[x];
}
}func validTree(n int, edges [][]int) bool {
if len(edges) != n-1 {
return false
}
parent := make([]int, n)
rank := make([]int, n)
for i := 0; i < n; i++ {
parent[i] = i
}
var find func(int) int
find = func(x int) int {
if parent[x] != x {
parent[x] = find(parent[x])
}
return parent[x]
}
for _, e := range edges {
ru, rv := find(e[0]), find(e[1])
if ru == rv {
return false
}
if rank[ru] < rank[rv] {
parent[ru] = rv
} else if rank[ru] > rank[rv] {
parent[rv] = ru
} else {
parent[rv] = ru
rank[ru]++
}
}
return true
}class Solution {
public:
bool validTree(int n, vector<vector<int>>& edges) {
if ((int)edges.size() != n - 1) return false;
vector<int> parent(n), rank(n, 0);
iota(parent.begin(), parent.end(), 0);
function<int(int)> find = [&](int x) {
if (parent[x] != x) parent[x] = find(parent[x]);
return parent[x];
};
for (auto& e : edges) {
int ru = find(e[0]), rv = find(e[1]);
if (ru == rv) return false;
if (rank[ru] < rank[rv]) parent[ru] = rv;
else if (rank[ru] > rank[rv]) parent[rv] = ru;
else {
parent[rv] = ru;
rank[ru]++;
}
}
return true;
}
};class Solution:
def validTree(self, n: int, edges: list[list[int]]) -> bool:
if len(edges) != n - 1:
return False
parent = list(range(n))
rank = [0] * n
def find(x: int) -> int:
if parent[x] != x:
parent[x] = find(parent[x])
return parent[x]
for u, v in edges:
ru, rv = find(u), find(v)
if ru == rv:
return False
if rank[ru] < rank[rv]:
parent[ru] = rv
elif rank[ru] > rank[rv]:
parent[rv] = ru
else:
parent[rv] = ru
rank[ru] += 1
return Truefunction validTree(n, edges) {
if (edges.length !== n - 1) return false;
const parent = Array.from({ length: n }, (_, i) => i);
const rank = Array(n).fill(0);
const find = (x) => {
if (parent[x] !== x) parent[x] = find(parent[x]);
return parent[x];
};
for (const [u, v] of edges) {
const ru = find(u), rv = find(v);
if (ru === rv) return false;
if (rank[ru] < rank[rv]) parent[ru] = rv;
else if (rank[ru] > rank[rv]) parent[rv] = ru;
else {
parent[rv] = ru;
rank[ru]++;
}
}
return true;
}
Comments