← 返回题目列表

如何判断一个无向图是不是一棵树?

高频 中等 第 6 / 30 题 更新于 2026/07/30
DFSBFS并查集

简化版

无向图是树需要同时满足两个条件:连通、无环。等价地,对 n 个节点来说,如果边数不是 n - 1,一定不是树;边数为 n - 1 时,再确认所有节点连通即可。

详细版

常用做法有两类。DFS/BFS:先判断边数是否为 n - 1,再从 0 出发遍历,最后看是否访问了 n 个节点。并查集:逐条合并边,如果发现两个端点已经在同一集合,说明有环;最后还要保证边数为 n - 1 或连通块数为 1。

树的性质很多,但面试最好抓“连通 + 无环”。边数检查是强剪枝:无向树恰好有 n - 1 条边,边多必有环,边少必不连通。

完整版教学

一、树在无向图里的定义

一棵无向树不是“长得像树”的图,而是满足连通且无环的无向图。连通表示任意两个节点之间都有路径;无环表示不存在从某个节点出发绕一圈回到自己的简单环。

树:
0 - 1 - 2
    |
    3

非树:
0 - 1
|   |
3 - 2   有环

这两个条件缺一不可。只有无环但不连通,是森林;只有连通但有环,是一般连通图,都不是树。

二、边数 n-1 为什么重要

无向树有 n 个节点时,边数一定是 n - 1。这可以从构造理解:第一个节点不需要边,每加入一个新节点,为了保持连通且不成环,只能用一条边把它接到已有树上,所以加入 n-1 次节点,得到 n-1 条边。

边数可能情况是否可能是树
小于 n-1不足以连通所有节点
等于 n-1还需检查连通或无环可能
大于 n-1连通情况下必有环

例如 n=5 的树必须有 4 条边。只有 3 条边最多连接成森林;有 5 条边则一定多出连接,可能形成环。

三、DFS/BFS 判断连通

有了边数 n - 1 这个前提,只要再确认图连通,就能判断是树。因为边数刚好,连通图不可能再有环;否则连通且有环至少会多出一条边。

boolean validTree(int n, int[][] edges) {
    if (edges.length != n - 1) return false;
    List<Integer>[] adj = new ArrayList[n];
    for (int i = 0; i < n; i++) adj[i] = new ArrayList<>();
    for (int[] e : edges) {
        adj[e[0]].add(e[1]);
        adj[e[1]].add(e[0]);
    }
    boolean[] seen = new boolean[n];
    Queue<Integer> q = new ArrayDeque<>();
    q.offer(0);
    seen[0] = true;
    int count = 0;
    while (!q.isEmpty()) {
        int u = q.poll();
        count++;
        for (int v : adj[u]) {
            if (!seen[v]) {
                seen[v] = true;
                q.offer(v);
            }
        }
    }
    return count == n;
}

这段代码没有显式判环,因为边数已经排除了边多的情况。这个简化是面试里非常值得讲清楚的点。

四、DFS 显式判环时要带父节点

如果不先用边数剪枝,也可以 DFS 检查无向图是否有环。无向边会从子节点指回父节点,所以 DFS 时遇到已访问节点不能立刻判环,必须排除父节点。

dfs(u, parent):
  mark u
  for v in adj[u]:
    if v == parent: continue
    if visited[v]: cycle
    dfs(v, u)

如果忘记 parent0-1 这条简单边会在 1 的邻接表里看到 0 已访问,从而被误判成环。无向图判环和有向图三色法不同,这是高频易错点。

五、并查集判断

并查集逐条处理边。如果 uv 已经在同一个集合,再合并会形成环,直接返回 false。处理完所有边后,如果边数是 n - 1 且没有发现环,就可以认为是树。

for each edge (u, v):
  if find(u) == find(v): return false
  union(u, v)
return edges.length == n - 1

也可以维护连通块数量,初始 components = n,每次成功 union 就减 1,最后要求 components == 1。并查集尤其适合边列表输入,不必先建邻接表。

六、复杂度和特殊情况

DFS/BFS 需要建邻接表,时间 O(V+E),空间 O(V+E)。并查集时间近似 O(E),空间 O(V)。如果 n=1 且没有边,应该返回 true,因为单个节点本身是一棵树。

方法优点注意点
BFS/DFS 连通性直观,适合讲树性质要先检查边数
DFS 判环 + 连通完整覆盖定义无向图要带 parent
并查集边列表场景简洁要处理连通性或边数

面试时推荐先说定义,再给边数剪枝加连通性遍历,这样逻辑最清楚。

七、常见误区与追问

记忆钩子:无向图是树等价于“边数刚好 n-1,并且所有点连成一片”。

  • 误区:只检查没有环。 没有环但可能不连通,那只是森林,不是树。
  • 误区:只检查边数等于 n-1。 边数对了仍可能不连通并有环,例如一个三角形加一个孤立点。
  • 误区:无向图 DFS 遇到访问过节点就判环。 必须排除父节点,否则普通双向边会被误判。
  • 追问:为什么边数小于 n-1 一定不连通? 每条边最多把两个连通块合成一个,n 个点连成一个块至少需要 n-1 条边。
  • 追问:并查集最后还要检查什么? 要确保没有环且整体连通;经典写法用 edges.length == n-1 加无环即可。
  • 追问:有向图的树怎么判断? 要考虑入度、根节点和可达性,不能直接套无向树条件。

八、加强记忆

判断无向图是不是树,不要只背某个算法,而要抓定义:连通且无环。实战中先用 edges.length == n - 1 快速过滤,再 DFS/BFS 检查从一个点能否访问全部节点;或用并查集逐边合并,发现同集合边就是环。边数、连通性、父节点,是这题的三个关键细节。