题目描述

✅ 面试题 04.04. 检查平衡性

image-20260929004917727

题意分析

判断二叉树的每个节点是否都满足左右子树高度差不超过 1。根节点看起来平衡并不能说明整棵树平衡,任意后代失衡都要返回失败。

解法:后序同时返回高度与失衡标记

核心思路

[!blue]

判断一个节点是否平衡需要先知道左右子树高度,所以从叶子向上做后序遍历。若在每个节点重新计算两侧高度,会反复访问同一批后代;让递归顺便返回高度,就能把所有判断合并到一次遍历中。

约定 dfs(node) 的返回值有两种含义:非负数表示这棵子树已经确认平衡,同时给出它的高度;-1 表示子树中至少有一个失衡节点。空树高度为 0,非空树高度至少为 1,因此 -1 不会与合法高度混淆。

取得左右结果 l、r 后,若任一结果为 -1,当前子树也不可能平衡,继续上传失败标记。否则两侧内部都已平衡,只需检查当前节点的高度差:超过 1 就返回 -1,不超过 1 才返回 max(l, r) + 1。

这样每层都同时验证了子树内部和当前根的条件,最终根返回非负值才说明所有节点都平衡。空树返回高度 0,因此也自然判为平衡。

解题步骤

  1. 空节点返回高度 0。
  2. 后序取得左右结果。
  3. 任一为 -1 或高度差超过 1,就返回 -1。
  4. 否则返回 max(left,right)+1;根结果非负表示平衡。

代码实现

class Solution {
    public boolean isBalanced(TreeNode root) {
        return dfs(root) >= 0;
    }

    // 返回子树高度;子树内存在失衡节点时返回 -1。
    private int dfs(TreeNode root) {
        if (root == null) {
            return 0;
        }

        int l = dfs(root.left);
        int r = dfs(root.right);

        if (l < 0 || r < 0 || Math.abs(l - r) > 1) {
            return -1;
        }

        return Math.max(l, r) + 1;
    }
}
func isBalanced(root *TreeNode) bool {
    // 返回子树高度;子树内存在失衡节点时返回 -1。
    var dfs func(*TreeNode) int
    dfs = func(root *TreeNode) int {
        if root == nil {
            return 0
        }

        l, r := dfs(root.Left), dfs(root.Right)
        if l == -1 || r == -1 || abs(l-r) > 1 {
            return -1
        }

        return max(l, r) + 1
    }
    return dfs(root) >= 0
}

func abs(x int) int {
    if x < 0 {
        return -x
    }
    return x
}

复杂度分析

  • 时间复杂度:$O(n)$,每个节点只计算一次高度和失衡状态。
  • 空间复杂度:$O(h)$,用于递归栈;树退化为链时为 $O(n)$。

关键点总结

[!green]

非负高度意味着整棵子树已验证通过,-1 使任意深处的失衡都能一路传到根;父节点无需重新遍历已经处理的后代。

易错点总结

[!yellow]

  • 不能只检查根的左右高度差。
  • 发现子树失衡后不能把 -1 当正常高度参与最终高度计算。
  • 平衡限制是高度差,不是左右节点数之差。

相似题目

题目 难度 关联与区别
104. 二叉树的最大深度 简单 复用后序求高度,再增加失衡标记的传播。
543. 二叉树的直径 简单 同样把子树高度作为返回值,并在父节点结合左右信息计算另一种树性质。
转载与许可
作者
链接 https://hgnulb.github.io/blog/2026/67672990
许可 本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!