题目描述
判断二叉树是否轴对称(沿根的竖线翻折后左右重合)。
思路解析
最直觉的想法:先复制整棵树、把每个节点的左右孩子翻转造出「镜像树」,再和原树一个一个比。能对,但要额外建一棵树、多走一遍,空间和代码都更重,纯属多此一举。
转折:对称的本质是「左子树」和「右子树」互为镜像,不必真造出镜像树,让一个递归同时握住左 L、右 R 两个节点对照走即可。规则:都空→对称;一空一非→不对称;否则要求 L.val==R.val,且 L 的左配 R 的右(外侧)、L 的右配 R 的左(内侧)。这题能这么做,是因为对称是「两两镜像位置」的局部判断,天然适合双节点同步递归。
出发 · 比根的两个孩子:根 1 自己不用比,直接拿它的左孩子 L=2 和右孩子 R=2 开镜像比较。两个都非空,先看值:2 == 2 ✓。转折点:不去真造镜像树,而是就让这一个递归同时握住 L、R 往下走。
值相等 · 拆出外侧、内侧两对:L=2、R=2 值相等后,对称要求交叉地往下递归两对:外侧 = L.left 配 R.right、内侧 = L.right 配 R.left。这两对都成立,整支才算镜像。先看外侧。
外侧配外侧 · L.left vs R.right:第一对外侧:左孩子的左 (3) 配 右孩子的右 (3)——这是离中线最远的一对。注意是交叉取:左的 left 对右的 right。3 == 3 ✓,且两边都是叶子,这一对镜像成立。
内侧配内侧 · L.right vs R.left:第二对内侧:左孩子的右 (4) 配 右孩子的左 (4)——靠近中线的一对,同样交叉取。4 == 4 ✓。外侧、内侧两对都成立,左 2 和右 2 这一支镜像完全通过。
左 2 这一支全部成立:左 2 与右 2 这一支:值相等、外侧相等、内侧相等,三项都成立,isMirror(左2, 右2) 返回 True。这棵小树只有这一支要比,根的两个孩子镜像即整树镜像。
处处相等 · 整棵对称:每一对镜像位置都相等,递归全程返回 True,整棵树对称。只要途中有一对不等或结构对不上,就会立刻短路返回 False。下面看看那种不对称的情况长什么样。
反例对照 · 把内侧右 4 改成 9:假设左孩子的右改成了 9。走到内侧这对:L.right=9 配 R.left=4,9 != 4 ✗。这一对镜像不成立。
一对不等 · 立刻 return False:只要任意一对镜像位置不相等,不必再比其余节点,直接 return False——递归靠 and 短路,一处假则全假。回到正例:正因为每一对都相等,才返回 True。
判断对称、判断两树相同、合并两棵树,都用「同时握住两个节点往下递归」的双指针式 DFS。对称就交叉(左配右)、相同就平行(左配左)——只差递归传参那一处。
参考代码(Python)
1def isMirror(L, R):
2 if not L and not R: return True # 都空,对称
3 if not L or not R: return False # 一空一非,结构不对
4 return (L.val == R.val and # 值要相等
5 isMirror(L.left, R.right) and # 外侧: 左的左 配 右的右
6 isMirror(L.right, R.left)) # 内侧: 左的右 配 右的左
7return isMirror(root.left, root.right) if root else True复杂度分析
- 时间复杂度:O(n) —— 每对镜像节点比较一次,共约 n 个节点
- 空间复杂度:O(h) —— 递归栈深度等于树高 h
套路模板
记住骨架:两个都空→真、一空一非→假、值等且子树两两递归。「相同的树(LC100)」把交叉改成平行 dfs(a.left,b.left) 就行。
1def dfs(a, b):
2 if not a and not b: return True # 都空
3 if not a or not b: return False # 结构不一致
4 return (a.val == b.val # 值相等
5 and dfs(a.left, b.right) # 对称: 外侧对外侧
6 and dfs(a.right, b.left)) # 相同的树(LC100)改成平行 a.left,b.left易错点
- 错误写法:对称写成平行 isMirror(L.left, R.left) → 正确写法:对称要交叉 isMirror(L.left, R.right)(镜像是外侧配外侧、内侧配内侧,平行比就成了判「两边长得一样」而非「镜像」)
- 错误写法:只比值不判空,遇 None 取 .val 报错 → 正确写法:先判空:都空→真、一空一非→假(结构不同(一边有孩子一边没有)也算不对称,且不先挡住 None 会崩)
以上文字与上方动画完全同一思路:先看动画建立直觉 → 读文字巩固 → 关掉页面自己默写一遍代码,能写出来才算真的掌握。