【力扣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)
核心思想 同步遍历对比外侧内侧 层序遍历配对对比
相关推荐
有点。3 小时前
C++03阶段练习(练习题)
数据结构·算法·图论
周末也要写八哥4 小时前
经典算法实例:游戏中弱角色的数量(二)
算法
是Yu欸5 小时前
鸿蒙PC移植:2048 从网页小游戏到 AI 桌面应用
大数据·人工智能·算法·数据挖掘·openharmony·codex
鹿角片ljp5 小时前
KV Cache 解析
java·算法
liliangcsdn5 小时前
IVOL与偏度因子的对比测量分析
算法
threerocks6 小时前
Jev 入门第一课
算法
西柚研究生1234567 小时前
论文分析17:YOLOv11_UAVNet:无人机航拍图像专用目标检测算法
人工智能·python·深度学习·算法·目标检测
hetao17338378 小时前
2026-09-17 hetao1733837 的刷题记录
c++·算法
午彦琳9 小时前
2026.9.17
数据结构·算法·leetcode
木井巳9 小时前
【记忆化搜索】不同路径
java·算法·leetcode·深度优先·剪枝·推荐算法