LeetCode 101. 对称二叉树
摘要
LeetCode 101 对称二叉树题解,使用栈(迭代法)成对比较镜像节点,判断二叉树是否轴对称,时间复杂度 O(n),空间复杂度 O(n)。
标签
#算法 #LeetCode #二叉树 #栈 #迭代 #题解
目录
- 题目描述
- 易错点
- 思路
- 编写代码
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. 易错点
- 空树视为对称,直接返回
true。 - 栈中存储的是成对的节点指针,每次弹出两个节点进行比较。
- 压栈顺序必须严格按照镜像对称关系,否则比较逻辑会错乱。
- 当两个节点都为空时,跳过此次循环,继续比较下一对。
- 当其中一个为空或值不相等时,立即返回
false并释放栈内存。 - 遍历结束后释放栈内存,避免内存泄漏。
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(镜像对应)
- 第一次弹出:
实际上,压栈操作是将"需要比较的两对节点"依次压入栈中。由于每次弹出两个节点,栈的设计保证了它们被正确配对。
与传统遍历的区别:
传统前序/中序/后序遍历每次只压入一个节点,处理的逻辑是"当前节点的子节点"。而本题每次压入四个节点(两对),出栈时按两两配对的方式进行比较,栈顶的两个节点(先弹出的一对)和次顶的两个节点(后弹出的一对)分别代表了左右子树的两个镜像分支。
这种"成对压栈、成对弹出"的模式使得栈能够模拟递归的成对比较过程,同时避免了递归栈可能带来的栈溢出风险。
算法步骤
-
若
root为空,返回true。 -
初始化栈,将
root->left和root->right压入栈(作为第一对待比较节点)。 -
循环执行以下操作,直到栈空:
-
从栈中弹出两个节点
l(左节点)和r(右节点)。 -
若
l和r都为空,则跳过此次循环。 -
若其中一个为空或值不相等,则返回
false。 -
否则按镜像对称顺序压入子节点:
- 先压
l->left,再压r->right(这对会在后面被一起弹出比较) - 再压
l->right,再压r->left(这对也会在后面被一起弹出比较)
- 先压
-
-
循环正常结束,返回
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;
}