【力扣100题】29. 对称二叉树

一、题目描述

给你一个二叉树的根节点 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

进阶

你可以运用递归和迭代两种方法解决这个问题吗?


二、解题思路总览

方法 核心思想 时间复杂度 空间复杂度
递归 同步遍历左右子树,外侧对内侧 O(n) O(h),h为树高
迭代 层序遍历,对比每层是否对称 O(n) O(n)

对称的核心

  • 外侧对内侧:left->left vs right->right
  • 内侧对外侧:left->right vs right->left
  • 两组同时满足才对称

三、完整代码

方法一:递归

cpp 复制代码
class Solution {
public:
    bool traversal(TreeNode* list1, TreeNode* list2) {
        // 1. 两节点都为空,对称
        if (list1 == NULL && list2 == NULL) return true;
        // 2. 只有一个为空,不对称
        else if (list1 == NULL || list2 == NULL) return false;
        // 3. 节点值不同,不对称
        else if (list1->val != list2->val) return false;

        // 4. 外侧对比:list1的左 vs list2的右
        bool outside = traversal(list1->left, list2->right);
        // 5. 内侧对比:list1的右 vs list2的左
        bool inside = traversal(list1->right, list2->left);

        // 6. 外侧和内侧都对称才对称
        return outside & inside;
    }

    bool isSymmetric(TreeNode* root) {
        if (!root) return true;                        // 空树
        return traversal(root->left, root->right);    // 对比左右子树
    }
};

方法二:迭代(层序遍历)

cpp 复制代码
class Solution {
public:
    bool isSymmetric(TreeNode* root) {
        if (!root) return true;           // 空树
        queue<TreeNode*> q;               // 层序遍历队列
        q.push(root->left);
        q.push(root->right);

        while (!q.empty()) {
            TreeNode* left = q.front();   // 左子树节点
            q.pop();
            TreeNode* right = q.front();  // 右子树节点
            q.pop();

            // 两节点都为空,继续
            if (!left && !right) continue;

            // 只有一个为空,不对称
            if (!left || !right) return false;

            // 值不同,不对称
            if (left->val != right->val) return false;

            // 入队:外侧对内侧
            q.push(left->left);   // 左子树的左
            q.push(right->right);// 右子树的右

            // 入队:内侧对外侧
            q.push(left->right);  // 左子树的右
            q.push(right->left);  // 右子树的左
        }

        return true;
    }
};

四、算法流程图(ASCII)

对称结构示意

复制代码
对称树示例:[1,2,2,3,4,4,3]

        1
       / \
      2   2      ← 根节点左右对称
     /\  /\
    3  44  3
     \  |  /
      镜像关系

外侧对比:2(左)->left(3)  vs  2(右)->right(3)
内侧对比:2(左)->right(4) vs  2(右)->left(4)

递归版流程

复制代码
isSymmetric(1)
        │
        ▼
traversal(2, 2)  ← 比较左右子树根节点
        │
        ├── 比较2.val == 2.val ✓
        │
        ├── outside = traversal(3, 3) ← 外侧
        │       ├── 比较3.val == 3.val ✓
        │       ├── outside = traversal(NULL, NULL) → true
        │       ├── inside = traversal(NULL, NULL) → true
        │       └── return true & true = true
        │
        └── inside = traversal(4, 4) ← 内侧
                ├── 比较4.val == 4.val ✓
                ├── outside = traversal(NULL, NULL) → true
                ├── inside = traversal(NULL, NULL) → true
                └── return true & true = true

最终:true & true = true

五、逐行解析

递归版逐行解析

cpp 复制代码
class Solution {
public:
    bool traversal(TreeNode* list1, TreeNode* list2) {
        // ─────────────────────────────────────────
        // 第1步:两节点都为空,对称
        // 递归终止条件:最简单的对称情况
        // ─────────────────────────────────────────
        if (list1 == NULL && list2 == NULL) return true;

        // ─────────────────────────────────────────
        // 第2步:只有一个为空,不对称
        // 一个有值一个为空,必定不对称
        // ─────────────────────────────────────────
        else if (list1 == NULL || list2 == NULL) return false;

        // ─────────────────────────────────────────
        // 第3步:节点值不同,不对称
        // ─────────────────────────────────────────
        else if (list1->val != list2->val) return false;

        // ─────────────────────────────────────────
        // 第4步:外侧对比
        // list1的左孩子 vs list2的右孩子
        // 对称树:左子树的左 == 右子树的右
        // ─────────────────────────────────────────
        bool outside = traversal(list1->left, list2->right);

        // ─────────────────────────────────────────
        // 第5步:内侧对比
        // list1的右孩子 vs list2的左孩子
        // 对称树:左子树的右 == 右子树的左
        // ─────────────────────────────────────────
        bool inside = traversal(list1->right, list2->left);

        // ─────────────────────────────────────────
        // 第6步:外侧和内侧同时满足才对称
        // 用 & 而不是 &&:确保两边都计算完再判断
        // ─────────────────────────────────────────
        return outside & inside;
    }

    bool isSymmetric(TreeNode* root) {
        if (!root) return true;  // 空树是对称的

        // 从左右子树开始对比
        return traversal(root->left, root->right);
    }
};

六、复杂度分析

时间复杂度

方法 分析 复杂度
递归 每个节点访问一次 O(n)
迭代 每个节点访问一次 O(n)

空间复杂度

方法 分析 复杂度
递归 函数调用栈,最大深度为树高h O(h)
迭代 队列存储同层节点 O(n)

七、面试追问 FAQ

问题 回答
为什么递归要用 & 而不是 && & 按位与确保两边表达式都执行完再返回;&& 可能有短路效应
递归的终止条件有哪些? 两节点都空(对称)、只有一个空(不对称)、值不同(不对称)
迭代怎么保证对称? 层序遍历时,每次取出两个节点对比,外侧入队和内侧入队要配对
和翻转二叉树有什么区别? 对称是左vs右,翻转是交换左右孩子,思路类似但用途不同
如何记忆这个算法? 记住"外侧对内侧"的口诀:left.left vs right.right,left.right vs right.left

八、相关题目

题目 难度 关键点
101. 对称二叉树 简单 本题
100. 相同的树 简单 递归比较
226. 翻转二叉树 简单 交换子树
104. 二叉树的最大深度 简单 递归遍历

九、总结

对比项 递归 迭代
代码量 较多
时间复杂度 O(n) O(n)
空间复杂度 O(h) O(n)
核心思想 同步遍历对比外侧内侧 层序遍历配对对比
相关推荐
大熊背1 小时前
近期遇到的一些问题总结(四)
算法·拍照·白平衡·isp pipeline
吴声子夜歌1 小时前
Java——Arrays
java·算法·排序算法
Purple Coder1 小时前
面试-第一篇心态篇
面试·职场和发展
Tutankaaa2 小时前
从单场到多场并发:知识竞赛平台的弹性扩展能力
服务器·笔记·学习·职场和发展
洛水水2 小时前
【力扣100题】26. 二叉树的中序遍历
算法·leetcode·深度优先
sheeta19982 小时前
LeetCode 每日一题笔记 日期:2026.05.11 题目:2553. 分割数组中数字的数位
笔记·算法·leetcode
ZPC82102 小时前
规划后的轨迹,如何发给 moveit_servo 执行
c++·人工智能·算法·3d
70asunflower2 小时前
5.4 分布分析
人工智能·算法·机器学习·数据挖掘·数据分析
Pkmer2 小时前
滑动窗口专题
算法