对称二叉树:别再傻傻地遍历了,这才是面试官想看的解法!
摘要:判断二叉树是否对称,你还在想着把左右子树遍历出来比对吗?太慢啦!本文带你用最优雅的递归法(双指针思想)解决 LeetCode 101 题。通过拆解“镜像”的本质,配合保姆级代码注释,让你彻底搞懂这道高频面试题!
前言
哈喽大家好,我是爱摸鱼的打工仔。
今天我们来聊聊 LeetCode 上的一道经典题目——101. 对称二叉树。
这道题看似简单,很多小伙伴第一反应可能是:“把左子树遍历一遍,把右子树遍历一遍,然后反转其中一个数组比对一下不就行了?”
打住!虽然这也能做,但不仅空间复杂度高,而且代码写起来啰嗦。面试官通常想看到的是你对树结构和递归逻辑的深刻理解。
今天,咱们就用最优雅的递归法,手把手教你如何让代码像诗一样优美。
核心知识点:什么是“镜像对称”?
在做题之前,我们得先达成一个共识:什么叫对称?
就像我们要照镜子一样,一棵树如果是对称的,意味着它的左子树和右子树是互为镜像的。
那么,两个树互为镜像,需要满足什么条件呢?
- 根节点的值必须相同。
- 树 A 的左子树必须和树 B 的右子树镜像。
- 树 A 的右子树必须和树 B 的左子树镜像。
大家发现没有?这本身就是一个递归的定义!
通俗理解:
想象两个小人,一个站在左子树,一个站在右子树。
- 他们俩的值要一样。
- 左边小人的“左手”(左孩子),要和右边小人的“右手”(右孩子)一样(这是外层)。
- 左边小人的“右手”(右孩子),要和右边小人的“左手”(左孩子)一样(这是内层)。
解题思路:双树递归(辅助函数法)
既然我们要比较的是“左子树”和“右子树”,那我们的递归函数就不应该只接收一个节点,而是接收两个节点!
步骤拆解:
-
主函数:
isSymmetric负责处理边界情况(比如根节点为空),然后启动递归,传入root.left和root.right。 -
辅助函数:
check(left, right)负责具体的比对逻辑。-
终止条件 1:如果两个人都走到底了(都为空),说明这一路没问题,返回
True。 -
终止条件 2:如果一个人走到底了,另一个还没(一个空一个不空),那肯定不对称,返回
False。 -
终止条件 3:两个人的值不一样,不对称,返回
False。 -
递归推进:如果上面都没问题,那就继续往里层探测:
- 检查外侧:
left.leftvsright.right - 检查内侧:
left.rightvsright.left - 两者都满足才算
True。
- 检查外侧:
-
代码实战:Python 实现
来,直接上代码。这份代码逻辑清晰,注释拉满,直接拿去背!
# Definition for a binary tree node.
# class TreeNode:
# def __init__(self, val=0, left=None, right=None):
# self.val = val
# self.left = left
# self.right = right
class Solution:
def isSymmetric(self, root: Optional[TreeNode]) -> bool:
# 如果根节点为空,按照定义通常认为是对称的
if not root:
return True
# 核心入口:我们要检查的是根节点的左孩子和右孩子是否互为镜像
return self.check(root.left, root.right)
# 辅助函数:检查两棵树(p 和 q)是否互为镜像
def check(self, left: Optional[TreeNode], right: Optional[TreeNode]) -> bool:
# 1. 【递归出口】如果两个节点都为空,说明比对到了尽头,是对称的
if not left and not right:
return True
# 2. 【递归出口】如果其中一个为空,另一个不为空,肯定不对称
if not left or not right:
return False
# 3. 【递归出口】如果两个节点的值不相等,不对称
if left.val != right.val:
return False
# 4. 【核心递归逻辑】
# 此时 left 和 right 的值是相等的,我们需要继续检查它们的子节点
# 必须同时满足以下两个条件:
# A. 左节点的左孩子 和 右节点的右孩子 对称(检查外侧)
# B. 左节点的右孩子 和 右节点的左孩子 对称(检查内侧)
return self.check(left.left, right.right) and self.check(left.right, right.left)
总结与延伸
这道题的精髓在于打破惯性思维。
通常我们写树的递归,函数签名是 func(node),处理当前节点然后递归左右。但这道题我们需要同步处理两个节点,所以函数签名变成了 func(left, right)。
知识点复盘:
- 递归三要素:确定参数(两个节点)、确定终止条件(空值判断、值不等判断)、确定单层逻辑(内外侧比对)。
- 镜像定义:左对右,右对左。
学会了这个思路,以后遇到类似的“比较两棵树”的题目(比如 LeetCode 100. 相同的树),你也能举一反三,轻松搞定!
我是爱摸鱼的打工仔,咱们下道题见!👋