一、二叉树基础:从定义到特殊类型
平衡树基于二叉树衍生,面试中常先考察基础概念 —— 需先掌握二叉树的定义、性质及特殊类型(满二叉树、完全二叉树)。
1. 二叉树的核心定义
二叉树是「每个节点最多有两个子节点」的树结构,子节点分左子树(Left)和右子树(Right),可递归定义为:
- 空树,或
- 由根节点、左二叉树、右二叉树组成(左 / 右子树也需满足二叉树定义)。
可视化:普通二叉树结构
2. 二叉树的 3 个高频性质(面试必背)
- 第 i 层最大节点数:若根为第 1 层,第 i 层最多有 2^(i-1) 个节点(如第 3 层最多 4 个);
- 高度 h 的最大节点数:总节点数最多为 2^h - 1(满二叉树即达到此上限);
- 叶子节点与度 2 节点关系:对任意非空二叉树,度为 0 的叶子节点数 n0 = 度为 2 的节点数 n2 + 1(推导:总边数 = 总节点数 - 1,且边数 = 2n2 +n1,联立得 n0=n2+1)。
3. 特殊类型:满二叉树(面试高频)
(1)满二叉树的定义
满二叉树是「除叶子节点外,所有节点均有两个子节点」的二叉树,且所有叶子节点在同一层。
可视化:满二叉树结构(高度 h=3)
(2)满二叉树的 2 个关键公式(面试计算题常考)
- 若高度为 h,总节点数 n = 2^h - 1 → 反推高度 h = log2(n+1);
- 叶子节点数 n0 = 2^(h-1)(如 h=3 时,叶子数 = 4)。
(3)满二叉树 vs 完全二叉树(易混点辨析)
完全二叉树是「按层序填充,最后一层从左到右连续,中间不缺节点」的二叉树(可理解为 “满二叉树的前缀”),二者区别:
| 类型 | 核心区别 | 示例特征 |
|---|---|---|
| 满二叉树 | 所有层均填满,叶子在同一层 | 高度 3 时必 7 个节点 |
| 完全二叉树 | 最后一层左连续,上层均填满 | 高度 3 时可 5/6/7 个节点 |
可视化:完全二叉树(非满)
二、为什么需要平衡树?—— 从 BST 的问题说起
二叉搜索树(BST)是基于二叉树的有序结构,核心性质:左子树值<根<右子树值,中序遍历为有序序列。但BST 存在致命缺陷 —— 易退化:
- 当插入有序数据(如 1→2→3→4→5)时,BST 会退化为链表,此时查询 / 插入 / 删除时间复杂度从 O (logn) 暴跌至 O (n)。
可视化:BST 退化案例
平衡树的本质:在 BST 基础上增加「平衡约束」,通过自调整(旋转、颜色翻转等)维持树高为 O (logn),确保操作性能稳定。
三、面试高频平衡树 1:AVL 树(严格平衡)
AVL 树是最早的自平衡 BST,核心是「严格平衡约束」:
- 每个节点的平衡因子(左子树高 - 右子树高)∈ {-1,0,1}
- 树高严格控制在 O (logn),查找性能最优
AVL 树面试考点
- 优点:查找快(树高最矮);缺点:插入 / 删除需多次旋转(维护严格平衡成本高)。
- 应用场景:读多写少场景(如静态索引)。
- 代码高频片段:平衡因子计算与旋转触发:
// 计算节点高度
private int getHeight(Node node) {
return node == null ? 0 : node.height;
}
// 计算平衡因子
private int getBalance(Node node) {
return node == null ? 0 : getHeight(node.left) - getHeight(node.right);
}
// 右旋实现(LL型处理)
private Node rightRotate(Node y) {
Node x = y.left;
Node T2 = x.right;
// 旋转
x.right = y;
y.left = T2;
// 更新高度
y.height = Math.max(getHeight(y.left), getHeight(y.right)) + 1;
x.height = Math.max(getHeight(x.left), getHeight(x.right)) + 1;
return x; // 新根
}
四、面试高频平衡树 2:红黑树(近似平衡)
红黑树是工程中最常用的平衡树(如 Java TreeMap、Linux 内核),核心是「通过颜色规则维持近似平衡」,而非严格平衡因子。
1. 红黑树 5 条核心性质(面试必背)
- 每个节点非红即黑;
- 根节点是黑色;
- 叶子节点(NIL 空节点)是黑色;
- 红色节点的两个子节点必为黑色(无连续红节点);
- 从任意节点到其所有叶子的路径,黑色节点数相同(「黑高」一致)。
2. 红黑树 vs AVL 树(面试高频对比)
| 维度 | AVL 树 | 红黑树 |
|---|---|---|
| 平衡程度 | 严格(平衡因子 ±1) | 近似(黑高一致) |
| 树高 | 更矮(~1.44logn) | 略高(~2logn) |
| 查找性能 | 略优 | 优秀(差距可忽略) |
| 插入 / 删除成本 | 高(需多次旋转) | 低(1-2 次旋转 + 颜色翻转) |
| 工程应用 | 读多写少场景 | 通用场景(默认首选) |
五、其他平衡树(面试速记)
1. 伸展树(Splay Tree)
- 核心:无平衡约束,通过「伸展操作」将最近访问节点移到根(利用局部性原理)。
- 特点:均摊复杂度 O (logn),但最坏 O (n);实现简单(无需维护平衡因子 / 颜色)。
- 应用:缓存系统(频繁访问的节点更快获取)。
2. 替罪羊树(Scapegoat Tree)
- 核心:当子树大小超过父节点的 α 倍(α≈0.7),直接「重构子树」为完全平衡树(暴力但高效)。
- 特点:插入 / 删除均摊 O (logn),实现比红黑树简单。
六、面试题实战(结合图解)
题 1:判断一棵二叉树是否为平衡树(LeetCode 110)
思路:后序遍历,计算每个节点的左右子树高度,同时检查平衡因子。
public boolean isBalanced(TreeNode root) {
return getHeightAndCheck(root) != -1;
}
// 若平衡,返回高度;否则返回-1
private int getHeightAndCheck(TreeNode node) {
if (node == null) return 0;
int leftH = getHeightAndCheck(node.left);
if (leftH == -1) return -1; // 左子树已失衡
int rightH = getHeightAndCheck(node.right);
if (rightH == -1) return -1; // 右子树已失衡
// 检查当前节点平衡因子
if (Math.abs(leftH - rightH) > 1) return -1;
return Math.max(leftH, rightH) + 1;
}
题 2:满二叉树高度为 h,求叶子节点数(面试基础题)
解答:根据满二叉树公式,叶子节点数 = 2^(h-1)(如 h=3 时,叶子数 = 4)。
推导:满二叉树总节点数 = 2^h -1,且满二叉树中 n2 = n0 -1(二叉树通用性质),总节点数 = n0 +n1 +n2。因满二叉树无度 1 节点(n1=0),故 2^h -1 =n0 +0 +(n0-1) → 2n0=2^h → n0=2^(h-1)。
题 3:AVL 树插入后,如何确定旋转类型?
步骤:
- 插入节点后,从插入点向上找「第一个平衡因子绝对值>1 的节点」(失衡根);
- 计算失衡根的左子树平衡因子(LL/LR)或右子树平衡因子(RR/RL);
-
- 失衡根平衡因子>1(左子树高):
-
-
- 左子树平衡因子>0 → LL 型(右旋);
-
-
-
- 左子树平衡因子<0 → LR 型(先左旋左子,再右旋根);
-
-
- 失衡根平衡因子<-1(右子树高):
-
-
- 右子树平衡因子<0 → RR 型(左旋);
-
-
-
- 右子树平衡因子>0 → RL 型(先右旋右子,再左旋根)。
-