LeetCode 101. 对称二叉树

LeetCode 101. 对称二叉树

摘要

LeetCode 101 对称二叉树题解,使用栈(迭代法)成对比较镜像节点,判断二叉树是否轴对称,时间复杂度 O(n),空间复杂度 O(n)。

标签

#算法 #LeetCode #二叉树 #栈 #迭代 #题解

目录

  1. 题目描述
  2. 易错点
  3. 思路
  4. 编写代码

1. 题目描述

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

示例 1:

text

ini 复制代码
输入:root = [1,2,2,3,4,4,3]
输出:true

示例 2:

text

csharp 复制代码
输入:root = [1,2,2,null,3,null,3]
输出:false

提示:

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

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


2. 易错点

  1. 空树视为对称,直接返回 true。
  2. 栈中存储的是成对的节点指针,每次弹出两个节点进行比较。
  3. 压栈顺序必须严格按照镜像对称关系,否则比较逻辑会错乱。
  4. 当两个节点都为空时,跳过此次循环,继续比较下一对。
  5. 当其中一个为空或值不相等时,立即返回 false 并释放栈内存。
  6. 遍历结束后释放栈内存,避免内存泄漏。

3. 思路

核心思想

判断二叉树是否对称,本质是比较左子树 和右子树是否互为镜像。镜像比较的规则是:

  • 左子树的左孩子 对应 右子树的右孩子
  • 左子树的右孩子 对应 右子树的左孩子

栈的原理

本题使用栈(而非递归)来实现迭代比较。栈的作用类似于一个"待比较节点对"的任务队列(后进先出),每次取出两个节点进行比较,再将它们的子节点按镜像规则成对压栈。

为什么栈可以实现对称判断?

可以将栈理解为存储"比较任务"的容器。与普通二叉树遍历不同,遍历时每次只处理一个节点,而本题每次从栈中弹出两个节点进行比较。这两个节点是当前这一层需要判断是否对称的一对节点。

压栈过程是关键:

text

scss 复制代码
stack[top++] = l->left;   // 左子树的左孩子
stack[top++] = r->right;  // 右子树的右孩子  → 与上一行成对比较
stack[top++] = l->right;  // 左子树的右孩子
stack[top++] = r->left;   // 右子树的左孩子  → 与上一行成对比较

为什么是这种顺序?

  • 栈是后进先出的,意味着后压入的节点会先被弹出比较。

  • 入栈顺序为:l->left, r->right, l->right, r->left

  • 出栈顺序则变为:r->left 和 l->right 先被弹出(因为最后压入),然后才是 r->right 和 l->left。

  • 但这里的入栈顺序经过精心设计,使得弹出的每一对正好是需要比较的镜像节点:

    • 第一次弹出:r->left 和 l->right(镜像对应)
    • 第二次弹出:r->right 和 l->left(镜像对应)

实际上,压栈操作是将"需要比较的两对节点"依次压入栈中。由于每次弹出两个节点,栈的设计保证了它们被正确配对。

与传统遍历的区别:

传统前序/中序/后序遍历每次只压入一个节点,处理的逻辑是"当前节点的子节点"。而本题每次压入四个节点(两对),出栈时按两两配对的方式进行比较,栈顶的两个节点(先弹出的一对)和次顶的两个节点(后弹出的一对)分别代表了左右子树的两个镜像分支。

这种"成对压栈、成对弹出"的模式使得栈能够模拟递归的成对比较过程,同时避免了递归栈可能带来的栈溢出风险。

算法步骤

  1. 若 root 为空,返回 true。

  2. 初始化栈,将 root->left 和 root->right 压入栈(作为第一对待比较节点)。

  3. 循环执行以下操作,直到栈空:

    • 从栈中弹出两个节点 l(左节点)和 r(右节点)。

    • 若 l 和 r 都为空,则跳过此次循环。

    • 若其中一个为空或值不相等,则返回 false。

    • 否则按镜像对称顺序压入子节点:

      • 先压 l->left,再压 r->right(这对会在后面被一起弹出比较)
      • 再压 l->right,再压 r->left(这对也会在后面被一起弹出比较)
  4. 循环正常结束,返回 true。


4. 编写代码

c 运行

c

ini 复制代码
bool isSymmetric(struct TreeNode* root) {
    if (root == NULL) return true;
    struct TreeNode** stack = (struct TreeNode**)malloc(10000 * sizeof(struct TreeNode*));
    int top = 0; 
    stack[top++] = root->left;
    stack[top++] = root->right;

    while (top > 0) {
        struct TreeNode* r = stack[--top];
        struct TreeNode* l = stack[--top];
        if (l == NULL && r == NULL) continue;
        if (l == NULL || r == NULL || l->val != r->val) {
            free(stack);
            return false;
        }
        stack[top++] = l->left;
        stack[top++] = r->right;
        stack[top++] = l->right;
        stack[top++] = r->left;
    }

    free(stack);
    return true;
}
相关推荐
UIU1147 小时前
P1233 [ICPC 2001 Taejon R] 木棍加工
数据结构·c++·算法·动态规划
论文复现现场7 小时前
WRF、FEA 能用 L40S 加速吗?求解器检查、断点恢复与长期运行
java·数据库·算法·wrf·有限元分析·gpu计算·断点续算
ZhengEnCi7 小时前
用 AI 写代码为什么拼到最后还是拼人:算力瓶颈与领域专家提示词的本质差异
算法·架构
秋天的怀念_David9 小时前
第二十一夜 · 数字黑洞与西西弗斯的惩罚
java·程序人生·算法
碧海银沙音频科技研究院9 小时前
在PC电脑上用Whisper→Marian算法实现离线语音翻译功能
算法·whisper
niyongsheng10 小时前
3D 装箱算法工程化:数据结构、放置启发式与支撑规则
数据结构·算法·rust
j7~12 小时前
【穷举、深搜与回溯算法】一棵决策树讲透全排列与子集
数据结构·c++·算法·leetcode·回溯·递归
傅科摆 _ py13 小时前
DP,贪心,堆和栈,二分力扣复习
开发语言·python·算法
Shaoshing14 小时前
LeetCode100 42.接雨水
java·算法·leetcode
域限奇点访客14 小时前
用AI生成问卷的网站怎么选?看题目录入结构、题型映射和生成后的校验
算法