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->leftl->right 先被弹出(因为最后压入),然后才是 r->rightl->left

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

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

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

与传统遍历的区别:

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

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

算法步骤

  1. root 为空,返回 true

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

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

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

    • lr 都为空,则跳过此次循环。

    • 若其中一个为空或值不相等,则返回 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;
}
相关推荐
是Dream呀16 小时前
人眼看不出的伪造,AI如何识别?实测合合信息跨模态鉴伪与去反光技术
算法
QN1幻化引擎16 小时前
SFA 信号场注意力:用8KB参数换248x KV Cache压缩,边缘设备也能跑长序列
人工智能·算法·gitee·gitlab
段一凡-华北理工大学17 小时前
向量数据库实战:选型、调优与落地~系列文章03:向量相似度算法全解:余弦、欧氏、内积,到底该用哪个?
大数据·数据库·人工智能·算法·机器学习·向量相似度·高炉炼铁
cjr_xyi18 小时前
AT1202Contest_c binarydigit 题解
c语言·c++·算法
atunet19 小时前
从算法优化到系统加速的多层级思考7
算法
Navigator_Z20 小时前
LeetCode //C - 1156. Swap For Longest Repeated Character Substring
c语言·算法·leetcode
Reart20 小时前
Leetcode 1143.最长公共子序列(720)
后端·算法
无相求码20 小时前
const vs #define:C语言常量定义的差异
c语言·算法
先吃饱再说20 小时前
LeetCode 226. 翻转二叉树
算法