comments | difficulty | edit_url | tags | ||||
---|---|---|---|---|---|---|---|
true |
Medium |
|
You have a graph of n
nodes labeled from 0
to n - 1
. You are given an integer n and a list of edges
where edges[i] = [ai, bi]
indicates that there is an undirected edge between nodes ai
and bi
in the graph.
Return true
if the edges of the given graph make up a valid tree, and false
otherwise.
Example 1:
Input: n = 5, edges = [[0,1],[0,2],[0,3],[1,4]] Output: true
Example 2:
Input: n = 5, edges = [[0,1],[1,2],[2,3],[1,3],[1,4]] Output: false
Constraints:
1 <= n <= 2000
0 <= edges.length <= 5000
edges[i].length == 2
0 <= ai, bi < n
ai != bi
- There are no self-loops or repeated edges.
To determine whether it is a tree, the following two conditions must be met:
- The number of edges is equal to the number of nodes minus one;
- 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
The time complexity is
class Solution:
def validTree(self, n: int, edges: List[List[int]]) -> bool:
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:
pa, pb = find(a), find(b)
if pa == pb:
return False
p[pa] = pb
n -= 1
return n == 1
class Solution {
public:
bool validTree(int n, vector<vector<int>>& edges) {
vector<int> p(n);
iota(p.begin(), p.end(), 0);
function<int(int)> 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[pa] = pb;
--n;
}
return n == 1;
}
};
class Solution {
public:
vector<int> p;
bool validTree(int n, vector<vector<int>>& edges) {
p.resize(n);
for (int i = 0; i < n; ++i) p[i] = i;
for (auto& e : edges) {
int a = e[0], b = e[1];
if (find(a) == find(b)) return 0;
p[find(a)] = find(b);
--n;
}
return n == 1;
}
int find(int x) {
if (p[x] != x) p[x] = find(p[x]);
return p[x];
}
};
func validTree(n int, edges [][]int) bool {
p := make([]int, n)
for i := range p {
p[i] = i
}
var find func(int) int
find = func(x int) int {
if p[x] != x {
p[x] = find(p[x])
}
return p[x]
}
for _, e := range edges {
pa, pb := find(e[0]), find(e[1])
if pa == pb {
return false
}
p[pa] = pb
n--
}
return n == 1
}
/**
* @param {number} n
* @param {number[][]} edges
* @return {boolean}
*/
var validTree = function (n, edges) {
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) {
const pa = find(a);
const pb = find(b);
if (pa === pb) {
return false;
}
p[pa] = pb;
--n;
}
return n === 1;
};
We can also use depth-first search to determine whether there is a cycle. We can use an array false
.
The time complexity is
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
class Solution {
private List<Integer>[] g;
private Set<Integer> 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);
}
}
}
}
class Solution {
public:
bool validTree(int n, vector<vector<int>>& edges) {
if (edges.size() != n - 1) {
return false;
}
vector<int> g[n];
vector<int> vis(n);
function<void(int)> 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;
}
};
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
}
/**
* @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;
};