题目描述
思路解析动画文字版
把「失衡」用 -1 编码上抛,就是经典的「剪枝」:一旦确定不平衡,整棵树后续都不用再算了。
后序遍历:先把左子树彻底算完,再右子树,最后才轮到父亲。每个节点会留一条记录:左高、右高、它们的差、以及返回给父亲的高度。
轮到节点 12(橙色)。它是叶子,左右子树都空,左高 = 0、右高 = 0。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 12 算左右高度差:|0 - 0| = 0 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(0, 0) = 1——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 8(橙色)。先读它两个孩子已经算好的高度:左高 = 1,右高 = 0(子为空就记 0)。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 8 算左右高度差:|1 - 0| = 1 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(1, 0) = 2——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 9(橙色)。它是叶子,左右子树都空,左高 = 0、右高 = 0。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 9 算左右高度差:|0 - 0| = 0 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(0, 0) = 1——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 4(橙色)。先读它两个孩子已经算好的高度:左高 = 2,右高 = 1(子为空就记 0)。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 4 算左右高度差:|2 - 1| = 1 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(2, 1) = 3——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 10(橙色)。它是叶子,左右子树都空,左高 = 0、右高 = 0。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 10 算左右高度差:|0 - 0| = 0 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(0, 0) = 1——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 11(橙色)。它是叶子,左右子树都空,左高 = 0、右高 = 0。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 11 算左右高度差:|0 - 0| = 0 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(0, 0) = 1——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 5(橙色)。先读它两个孩子已经算好的高度:左高 = 1,右高 = 1(子为空就记 0)。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 5 算左右高度差:|1 - 1| = 0 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(1, 1) = 2——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 2(橙色)。先读它两个孩子已经算好的高度:左高 = 3,右高 = 2(子为空就记 0)。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 2 算左右高度差:|3 - 2| = 1 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(3, 2) = 4——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 3(橙色)。它是叶子,左右子树都空,左高 = 0、右高 = 0。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 3 算左右高度差:|0 - 0| = 0 ≤ 1 ✓ 平衡(转绿)。它返回给父亲的高度 = 1 + max(0, 0) = 1——「1」是它自己这一层,max 取左右里更深的那条链。
轮到节点 1(橙色)。先读它两个孩子已经算好的高度:左高 = 4,右高 = 1(子为空就记 0)。 这两个高度都是孩子在「它们自己那一帧」算好返回上来的——后序遍历保证算父亲前孩子一定先算完。
节点 1 算左右高度差:|4 - 1| = 3 > 1 —— 失衡!(转红)这个节点违反了「左右高度差 ≤ 1」,所以它返回特殊值 -1,表示这棵子树已经不平衡。-1 会一路往上抛,整棵树判定为「不平衡」。
空 / 单点天然平衡;一条链则在某节点处左右高度差 > 1 而失衡,都被这套递归正确覆盖。
认出「后序遍历 + 返回高度 + 顺手判条件」这个模板,一类二叉树题就通了。
参考代码
class Solution: def isBalanced(self, root): def height(node): if not node: return 0 L = height(node.left) if L == -1: return -1 # 左子树已失衡,上抛 R = height(node.right) if R == -1: return -1 # 右子树已失衡,上抛 if abs(L - R) > 1: return -1 # 本节点失衡 return 1 + max(L, R) # 平衡则返回高度 return height(root) != -1复杂度
- 时间:O(n),自底向上每个节点只访问一次;失衡时还能提前剪枝
- 空间:O(h),递归栈深度 = 树高 h,最坏(退化成链)O(n)
易错点
面试追问把动画讲成自己的话
追问为什么用后序遍历?
追问用 -1 表示失衡有没有坑?
追问这套「递归返回高度、顺手判条件」的套路还能解什么?
这道题到这就讲完了。动画和文字是同一套思路——别光看,关掉页面自己默写一遍。然后顺着主线继续:
相同的树
LeetCode 100 · 简单 · 沿着 树 继续往下推进
把这道题真正学会,再走
图解算法年卡 ¥99 /年
- ✓本题每步动画的吴师兄语音讲解(全站陆续覆盖)
- ✓小欧带 8 步通关训练——追问到不看答案也能写对、能 30 秒讲给面试官听
- ✓学习报告,记录每道题的掌握程度
76k+ GitHub Star · 吴师兄开源图解算法,几十万开发者在看的算法讲解
想成体系刷透这类套路?去图解算法专题