力扣刷题#29-0101-对称二叉树
力扣刷题#29-0101-对称二叉树
题目
给你一个二叉树的根节点 root,检查它是否轴对称。
输入: root = [1,2,2,3,4,4,3]
1
/ \
2 2
/ \ / \
3 4 4 3
输出: true
输入: root = [1,2,2,null,3,null,3]
1
/ \
2 2
\ \
3 3
输出: false
我的思路之旅:从"想不通"到"搞懂"
第一反应:绕远的想法
看到这题,我试过"翻转左子树再和右子树比较"——逻辑上没错,但要写两个函数,很麻烦。
真正的盲区:我一直在想"怎么处理一棵子树",但对称是"两棵子树互为镜像"的关系。
判断函数要接收两个节点,交叉比较——左子树的外侧对应右子树的外侧,内侧对应内侧。
镜像的交叉对应
1
/ \
2 2
/ \ / \
3 4 4 3
比较对 1: 左的 left(3) ↔ 右的 right(3) ← 镜像的外侧
比较对 2: 左的 right(4) ↔ 右的 left(4) ← 镜像的内侧
生活化类比:左右手是否互为镜像?左手拇指对右手拇指,但位置是交叉的——照镜子就明白了。
解法一:递归版(我的代码)
class Solution {
public:
bool isMirror(TreeNode* n1, TreeNode* n2) {
if (!n1 || !n2)
return n1 == n2; // 如果至少一个为空,判断是否全空
if (n1->val != n2->val)
return false; // 如果值不同,返回false
return isMirror(n1->right, n2->left) && isMirror(n1->left, n2->right);
}
bool isSymmetric(TreeNode* root) {
return isMirror(root->left, root->right);
}
};
递归逻辑三层
| 层 | 判断 | 动作 |
|---|---|---|
| ① 出口 | 至少一个为空 | return n1 == n2(都空才对) |
| ② 值 | 值不等 | return false |
| ③ 递归 | 值相等 | 交叉比较:(右, 左) 且 (左, 右) |
我困惑的点:isMirror 怎么才能返回 true?
true 的种子在最底层:两个空指针相遇时判定为镜像。
isMirror(4, 4)
→ 值相等,继续
→ isMirror(null, null) ← 两个都空
→ return (null == null) = TRUE ✅
→ && isMirror(null, null) = TRUE ✅
→ 返回 true
true 从最底层一路 && 传回顶层。只要整棵树对称,每一层都成立。
我踩的坑:函数不能套函数
第一版我把 isMirror 定义在 isSymmetric 里面——标准 C++ 不允许函数内定义函数,编译报错。要写成类的私有成员函数。
解法二:迭代版(我的代码)
class Solution {
public:
bool isSymmetric(TreeNode* root) {
queue<TreeNode*> qu;
qu.push(root->left);
qu.push(root->right);
while (!qu.empty()) {
TreeNode* n1 = qu.front(); qu.pop();
TreeNode* n2 = qu.front(); qu.pop();
// 判断是否为镜像,先处理不是的情况
if (!n1 && !n2) continue; // 都空,处理队列中其他的
if (!n1 || !n2) return false; // 一个空,返回false
if (n1->val != n2->val) return false; // 值不等,返回false
// 走到这里说明两个节点为镜像,入队交叉对
qu.push(n1->left); qu.push(n2->right);
qu.push(n1->right); qu.push(n2->left);
}
// 走完队列没有返回false,说明全部为镜像
return true;
}
};
思路:递归的调用栈换成队列
迭代版把"要比较的节点对"放进队列,一次取一对出来比。递归的 isMirror(n1, n2) 变成队列里的 (n1, n2) 对。
我困惑的点:if 判断条件怎么写?
写 if 分支 = 穷举所有情况 + 安全排序 + 失败早退。
第一步:穷举。取出一对 (n1, n2),只有 4 种情况:
| 情况 | n1 | n2 | 结论 |
|---|---|---|---|
| ① | 空 | 空 | 这对匹配 ✅ continue |
| ② | 空 | 非空 | 不镜像 ❌ false |
| ③ | 非空 | 空 | 不镜像 ❌ false |
| ④ | 非空 | 非空 | 看值是否相等 |
第二步:安全排序。判空永远在判值前面——空指针不能访问 ->val。
第三步:失败早退。所有失败情况提前 return false。
我困惑的点:什么时候 return false / return true?
false 可以提前走,true 必须最后走。
| 位置 | 返回什么 | 原因 |
|---|---|---|
| 循环内发现违规 | return false |
错误是决定性的,发现即定案 |
| 循环内全部通过 | 不能 return true | 后面可能还有不匹配的 |
| 循环彻底走完 | return true |
所有节点对都查过且通过 |
通用心法:
循环 {
if (不满足) return false; // 违反就早退
}
return true; // 全部检查完才返回 true
"对称"是全称判断——必须所有节点对都通过才能下结论。中途的"暂时通过"随时可能被后面的推翻。
递归 vs 迭代
| 对比 | 递归 | 迭代 |
|---|---|---|
| 数据结构 | 函数调用栈 | 队列 |
| 对应关系 | isMirror(n1, n2) |
队列存 (n1, n2) 对 |
| 交叉入队 | isMirror(右, 左) && isMirror(左, 右) |
qu.push(右); qu.push(左) 交叉 |
| 空对处理 | 出口 n1==n2 |
continue |
| 失败 | 逐层返回 false | 立即 return false |
复杂度分析
| 维度 | 递归 | 迭代 |
|---|---|---|
| 时间复杂度 | O(n) | O(n) |
| 空间复杂度 | O(h) 递归栈 | O(n) 队列 |
关键点总结
| 关键点 | 说明 |
|---|---|
| 核心思路 | 对称 = 两棵子树互为镜像,交叉比较 |
| 交叉对 | (左外, 右外) 和 (左内, 右内) |
| 判断心法 | 穷举 + 先判空后判值 + 失败早退 |
| true/false 时机 | false 提前走,true 最后走 |
| 递归换迭代 | DFS 用栈模拟,BFS 用队列 |
本文档由 AI 辅助生成,作者提供问题,思路和代码,AI仅负责文本修饰,综合获得以上内容。

浙公网安备 33010602011771号