1. 题目解析与核心思路
LeetCode 116题"相同的树"和"对称二叉树"是二叉树类问题中的经典题目,考察了对二叉树结构的理解和递归算法的应用能力。这两个问题看似简单,但包含了二叉树遍历和比较的核心思想。
1.1 相同的树问题
给定两棵二叉树的根节点p和q,判断它们是否完全相同。这里的"相同"指的是:
- 两棵树的结构完全相同
- 对应节点的值完全相同
1.2 对称二叉树问题
给定一棵二叉树的根节点root,判断这棵树是否是镜像对称的。即:
- 左子树和右子树互为镜像
- 对应节点的值相同
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 递归解法详解
递归是解决这类树形结构问题最自然的方式,因为它完美匹配了树本身的递归定义。
2.1 相同的树递归解法
python复制def isSameTree(p, q):
# 基本情况1:两棵树都为空
if not p and not q:
return True
# 基本情况2:其中一棵树为空
if not p or not q:
return False
# 基本情况3:节点值不相等
if p.val != q.val:
return False
# 递归比较左右子树
return isSameTree(p.left, q.left) and isSameTree(p.right, q.right)
关键点解析:
- 基本情况处理:空树情况优先判断
- 节点值比较:当前节点值必须相等
- 递归调用:同时比较左右子树
2.2 对称二叉树递归解法
python复制def isSymmetric(root):
if not root:
return True
return isMirror(root.left, root.right)
def isMirror(left, right):
# 基本情况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
# 递归比较:左子树的左节点与右子树的右节点
# 以及左子树的右节点与右子树的左节点
return isMirror(left.left, right.right) and isMirror(left.right, right.left)
关键点解析:
- 基本情况处理与相同树类似
- 递归比较时是交叉比较:left.left vs right.right
- 对称比较是镜像比较,不同于相同树的直接比较
3. 迭代解法实现
虽然递归解法简洁,但理解迭代解法有助于深入掌握树的遍历方式。
3.1 相同的树迭代解法
使用队列进行层次遍历比较:
python复制from collections import deque
def isSameTree(p, q):
queue = deque()
queue.append((p, q))
while queue:
node1, node2 = queue.popleft()
if not node1 and not node2:
continue
if not node1 or not node2:
return False
if node1.val != node2.val:
return False
queue.append((node1.left, node2.left))
queue.append((node1.right, node2.right))
return True
3.2 对称二叉树迭代解法
同样使用队列,但比较方式不同:
python复制from collections import deque
def isSymmetric(root):
if not root:
return True
queue = deque()
queue.append(root.left)
queue.append(root.right)
while queue:
left = queue.popleft()
right = queue.popleft()
if not left and not right:
continue
if not left or not right:
return False
if left.val != right.val:
return False
queue.append(left.left)
queue.append(right.right)
queue.append(left.right)
queue.append(right.left)
return True
4. 复杂度分析与优化
4.1 时间复杂度
两种方法的时间复杂度都是O(n),其中n是树中节点的数量,因为每个节点都会被访问一次。
4.2 空间复杂度
- 递归解法:最坏情况下空间复杂度是O(h),h是树的高度,由递归调用栈深度决定
- 迭代解法:空间复杂度取决于队列中存储的节点数量,最坏情况下也是O(n)
4.3 优化思路
- 短路优化:在递归或迭代过程中,一旦发现不匹配立即返回,避免不必要的计算
- 并行遍历:可以尝试同时遍历两棵树,边遍历边比较
- 序列化比较:将树序列化为字符串后比较,但这种方法效率通常不如直接遍历
5. 常见错误与调试技巧
5.1 常见错误类型
- 空指针异常:未正确处理空节点情况
- 比较顺序错误:对称比较时左右子树比较方向错误
- 递归终止条件不完整:缺少必要的终止条件导致无限递归
- 值比较遗漏:只比较了结构而忘记比较节点值
5.2 调试技巧
- 打印递归路径:在递归函数中添加打印语句,跟踪比较过程
- 可视化小树:手动构建3-4个节点的小树,逐步验证
- 单元测试:编写各种边界情况的测试用例
- 两棵空树
- 一棵空树一棵非空
- 结构相同值不同
- 值相同结构不同
- 使用树的可视化工具:如Graphviz绘制树结构辅助理解
6. 相关题目拓展
掌握了这两个基础问题后,可以尝试以下LeetCode题目:
-
- 相同的树(比116更基础)
-
- 对称二叉树
-
- 另一棵树的子树
-
- 翻转二叉树
-
- 合并二叉树
这些题目都基于相似的递归思想和树遍历技巧,是巩固二叉树知识的良好练习。
7. 实际应用场景
理解树的结构比较在实际开发中有广泛应用:
- 文件系统比较:比较两个目录结构是否相同
- 配置验证:验证生成的配置树是否符合预期
- 语法树分析:比较两段代码的抽象语法树
- 游戏状态判断:判断游戏状态树是否对称平衡
- 机器学习决策树:比较决策树模型的结构差异
8. 个人经验分享
在实际解决这类问题时,我发现以下几点特别重要:
- 先处理边界情况:空树、单节点树等特殊情况先考虑
- 画图辅助:在纸上画出小规模的树结构,手动模拟算法流程
- 递归思考:把大问题分解为子问题,相信递归函数能解决子问题
- 测试驱动:先写测试用例再实现,确保覆盖所有边界情况
- 复杂度意识:时刻考虑算法的时间和空间复杂度
对于递归解法,我通常会先写出终止条件,再处理当前节点,最后处理递归调用。这种"自顶向下"的思考方式能帮助我更清晰地组织代码逻辑。
