101. 对称二叉树 (Easy)

专题归类: 05-二叉树 LeetCode 链接: https://leetcode.cn/problems/symmetric-tree/


在线做题: 写 Python 代码并运行 · Java 跳转 LeetCode

题目描述

给你一个二叉树的根节点 root,检查它是否轴对称。

示例 1:

输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

  • 树中节点数目在范围 [1, 1000]
  • -100 <= Node.val <= 100

进阶: 你可以用递归和迭代两种方法解决吗?


题目详细分析

  • 数据范围含义: 节点数最多 1000,值范围 [-100, 100],递归深度最多 1000,安全。
  • 输入输出特征: 输入是整棵树的根节点,输出是布尔值 True/False。
  • 边界条件: 树只有根节点时返回 True(单个节点对称)。空树?题目提示节点数 >= 1。
  • 核心约束: 轴对称不是左右子树完全相同,而是镜像相同。比较的是:左.left == 右.right左.right == 右.left
  • 隐藏条件: 比较的是结构对称和值相等,不是引用相等。

小白版直白理解

就像检查一个人的脸是否左右对称——你不需要把左边脸复制到右边,而是要看左边的右半边是否等于右边的左半边。同理,检查二叉树对称:左子树的左孩子要和右子树的右孩子一样,左子树的右孩子要和右子树的左孩子一样。


解题思路

思路一:递归双指针法(推荐)

核心思想: 同时用两个指针 p 和 q,p 从根节点的左子树出发,q 从根节点的右子树出发。p 往左走时 q 往右走(镜像对称),p 往右走时 q 往左走。

为什么用这种特殊的遍历顺序? 普通的单指针遍历无法检查镜像对称——因为对称是需要「交叉对比」的。双指针法让两个指针按镜像轨迹移动,完美匹配对称的定义。

def isSymmetric(root):
    def check(p, q):
        # 两个都为空:对称
        if not p and not q:
            return True
        # 一个为空一个不为空:不对称
        if not p or not q:
            return False
        # 值不相等:不对称
        if p.val != q.val:
            return False
        # 递归检查:p 的左 vs q 的右,p 的右 vs q 的左
        return check(p.left, q.right) and check(p.right, q.left)
 
    return check(root.left, root.right)

思路二:迭代法(队列)

用队列成对地放入需要比较的节点。每次取出两个节点比较,然后按照镜像顺序放入它们的子节点。

为什么用队列? 队列可以保持成对比较的顺序,不需要递归调用栈,对树深度很大的情况更安全。

from collections import deque
 
def isSymmetric(root):
    q = deque([root.left, root.right])
    while q:
        p1 = q.popleft()
        p2 = q.popleft()
        # 两个都为空:继续检查下一对
        if not p1 and not p2:
            continue
        # 一个为空或值不等:不对称
        if not p1 or not p2 or p1.val != p2.val:
            return False
        # 成对入队(镜像顺序)
        q.extend([p1.left, p2.right])
        q.extend([p1.right, p2.left])
    return True

易错点

  • 比较方向搞反: 不是 check(p.left, q.left),而是 check(p.left, q.right)——因为是对称比较,左的左 vs 右的右。
  • 空节点判断顺序: 先判断「两者都空→True」,再判断「一个空→False」,顺序不能乱。如果先判断一个空会把两个都空的情况误判为 False。
  • 迭代法 continue 而非 return: 当两个都为空时要用 continue 跳过这对,而不是 return True,因为还有别的节点等待检查。
  • 值比较用 !=: 严格来说,对称要求值相等,所以 p.val != q.val 时返回 False。

框架提炼

双指针镜像比较模板:

当需要比较两个树是否对称/相同/相似时,使用双指针(或多指针)同时遍历两棵树:

def compare(p, q):
    if not p and not q:
        return True   # 都空 → 一致
    if not p or not q:
        return False  # 一个空 → 不一致
    if p.val != q.val:
        return False  # 值不同 → 不一致
    # 根据具体问题定义如何递归比较
    return compare(p.left, q.right) and compare(p.right, q.left)

这种模式可以扩展到「相同的树」(左右方向对应)、「子树判断」等问题。


关联题目

  • 226-翻转二叉树 — 翻转和对称是互逆操作:翻转后和自己相同则对称;对称树的左子树翻转后等于右子树。
  • 100-相同的树 — 双指针比较模板的简化版:p.left vs q.left, p.right vs q.right(相同方向比较)。
  • 104-二叉树的最大深度 — 同为二叉树递归基础,但本题的比较涉及两棵树之间的交叉比较。