1. 概述

树结构是计算机科学中最核心的数据结构之一。我们之所以特别关注平衡二叉树,是因为它具备极佳的性能特性——查询、插入、删除等操作的时间复杂度可以稳定在 O(log n)。

本文将带你实现一个高效的算法,用于判断一棵二叉树是否为平衡树。内容不搞花里胡哨,直奔主题,适合有经验的开发者快速回顾或踩坑参考。


2. 基本定义

先统一几个关键概念,避免理解偏差:

  • 二叉树(Binary Tree):每个节点最多有两个子节点的树结构
  • 树的高度(Height):从根节点到最深叶子节点的最长路径长度(即最大深度)
  • 平衡树(Balanced Tree):对于任意子树,其左子树和右子树的高度差不超过 1

✅ 正确理解:这里的“平衡”指的是高度平衡(height-balanced),不是左右节点数量相等。

举个例子,下面这棵二叉树就是平衡的:

binary tree

图中三条绿色边标出了从根到最深叶子的路径,数字表示层级。可以看出左右子树高度差为 1,满足平衡条件。


3. 数据结构设计

先定义树节点:

public class Tree {
    private int value;
    private Tree left;
    private Tree right;

    public Tree(int value, Tree left, Tree right) {
        this.value = value;
        this.left = left;
        this.right = right;
    }

    // 标准 getter 方法
    public int value() { return value; }
    public Tree left() { return left; }
    public Tree right() { return right; }
}

⚠️ 注意:为简洁起见,这里假设节点值为整数。leftrightnull 时表示当前节点是叶子节点。

接下来定义递归过程中的返回结果封装类:

private class Result {
    private boolean isBalanced;
    private int height;

    private Result(boolean isBalanced, int height) {
        this.isBalanced = isBalanced;
        this.height = height;
    }
}

这个 Result 类很关键,它同时携带两个信息:

  • 当前子树是否平衡
  • 当前子树的高度

这样可以避免重复遍历,提升效率。


4. 算法实现

核心思路:对每个节点进行递归检查,利用后序遍历(左右根)自底向上收集信息

我们采用深度优先搜索(DFS),在每层递归中:

  1. 先处理左右子树
  2. 获取子树的平衡状态和高度
  3. 判断当前节点是否满足平衡条件

递归逻辑

private Result isBalancedRecursive(Tree tree, int depth) {
    if (tree == null) {
        return new Result(true, -1); // 空树视为平衡,高度为 -1
    }

    // 递归处理左右子树
    Result leftSubtreeResult = isBalancedRecursive(tree.left(), depth + 1);
    Result rightSubtreeResult = isBalancedRecursive(tree.right(), depth + 1);

    // 判断当前节点是否平衡
    boolean isBalanced = Math.abs(leftSubtreeResult.height - rightSubtreeResult.height) <= 1;
    boolean subtreesAreBalanced = leftSubtreeResult.isBalanced && rightSubtreeResult.isBalanced;
    int height = Math.max(leftSubtreeResult.height, rightSubtreeResult.height) + 1;

    return new Result(isBalanced && subtreesAreBalanced, height);
}

关键点解析

  • 空节点处理:返回 isBalanced = trueheight = -1,这样叶子节点高度为 0,逻辑更统一
  • 高度差判断Math.abs(leftHeight - rightHeight) <= 1
  • 短路逻辑:只有当左右子树都平衡,且高度差 ≤1 时,当前树才算平衡

外部调用接口

为了简化使用,提供一个外观方法(facade):

public boolean isBalanced(Tree tree) {
    return isBalancedRecursive(tree, -1).isBalanced;
}

这样调用者无需关心递归细节和初始深度。


5. 总结

本文实现了一个简单粗暴但高效的判断二叉树是否平衡的算法:

  • ✅ 时间复杂度:O(n),每个节点访问一次
  • ✅ 空间复杂度:O(h),h 为树高,递归栈深度
  • ✅ 关键技巧:一次递归返回多个信息,避免重复计算

该实现适用于大多数面试和实际场景。如果你在写递归时经常“卡住”,建议多模仿这种“封装返回值”的模式,能显著提升代码清晰度和正确性。

示例代码已托管至 GitHub:https://github.com/john-doe/tutorials/tree/master/algorithms/balanced-tree


原始标题:How to Determine if a Binary Tree Is Balanced in Java