力扣解题-101. 对称二叉树
给你一个二叉树的根节点 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
进阶:你可以运用递归和迭代两种方法解决这个问题吗?
Related Topics
树、深度优先搜索、广度优先搜索、二叉树
第一次解答
解题思路
核心方法:递归法(深度优先搜索 DFS),将“判断整棵树是否对称”拆解为“判断左右子树是否镜像对称”,通过辅助函数逐节点校验镜像位置的节点是否满足“结构相同+值相同”,逻辑清晰且时间/空间复杂度最优。
核心逻辑拆解(通俗版)
判断二叉树轴对称的核心是“镜像对比”——左子树的左节点对应右子树的右节点,左子树的右节点对应右子树的左节点,需同时满足结构对称和值对称:
- 主函数处理根节点:
- 若根节点
root == null(空树),直接返回true(空树天然对称); - 调用辅助函数
isCompare(root.left, root.right),校验左右子树是否镜像对称;
- 若根节点
- 辅助函数镜像校验:
- 基线条件1(双空节点):
left == null && right == null,镜像位置都无节点,返回true; - 基线条件2(结构不对称):
left == null || right == null(仅一个节点为空),返回false; - 基线条件3(值不对称):
left.val != right.val,返回false; - 递归校验深层镜像节点:
isCompare(left.left, right.right):左子树的左节点 ↔ 右子树的右节点;isCompare(left.right, right.left):左子树的右节点 ↔ 右子树的左节点;
- 合并结果:只有两层镜像校验都通过(
aResult && bResult),才返回true。
- 基线条件1(双空节点):
具体步骤(以示例1 root=[1,2,2,3,4,4,3]为例)
| 递归层级 | 对比节点(left,right) | 校验结果 | 说明 |
|---|---|---|---|
| 1 | (2,2) | - | 值相同,递归校验深层 |
| 2 | (3,3) | true | 双叶子节点,结构+值对称 |
| 2 | (4,4) | true | 双叶子节点,结构+值对称 |
| 3 | (null,null) | true | 镜像位置都为空 |
| 最终结果为true,与示例一致。 |
性能说明
- 时间复杂度:O(n)(n为节点总数,每个节点仅被对比一次);
- 空间复杂度:O(h)(h为树的高度,递归调用栈的深度等于树的高度):
- 最好情况(平衡二叉树):h = log₂n,空间复杂度O(logn);
- 最坏情况(斜树):h = n,空间复杂度O(n);
- 优势:
- 镜像对比逻辑精准,辅助函数将对称判断拆解为原子问题,易理解;
- 短路特性:只要某一层级校验失败,后续递归直接终止,执行效率高;
- 天然处理空树、单节点树等边界场景。
public boolean isSymmetric(TreeNode root) {
if(root==null){
return true;
}
return isCompare(root.left,root.right);
}
public boolean isCompare(TreeNode left, TreeNode right) {
if(left==null && right==null){
return true;
}
if(left==null && right !=null){
return false;
}
if(left!=null && right==null){
return false;
}
if(left.val != right.val){
return false;
}
boolean aResult=isCompare(left.left,right.right);
boolean bResult=isCompare(left.right,right.left);
return aResult && bResult;
}
示例解答
解题思路
解法1:迭代法(广度优先搜索 BFS / 层序遍历)
核心方法:队列辅助镜像层序对比,利用队列存储镜像位置的节点对,逐层校验每一对节点是否满足“结构+值对称”,属于“自顶向下”的迭代实现,避免递归栈的调用,满足进阶要求。
代码实现
import java.util.LinkedList;
import java.util.Queue;
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
// 队列存储镜像位置的节点对
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root.left);
queue.offer(root.right);
while (!queue.isEmpty()) {
// 取出一对镜像节点
TreeNode left = queue.poll();
TreeNode right = queue.poll();
// 双空节点,跳过后续校验
if (left == null && right == null) {
continue;
}
// 结构不对称或值不对称,直接返回false
if (left == null || right == null || left.val != right.val) {
return false;
}
// 按镜像顺序加入下一层节点对
queue.offer(left.left);
queue.offer(right.right);
queue.offer(left.right);
queue.offer(right.left);
}
// 所有镜像节点对比完成,均对称
return true;
}
核心逻辑说明
- 队列初始化:将根节点的左右子节点作为第一对镜像节点入队;
- 层序镜像对比:
- 每次取出一对节点,校验逻辑与递归法的辅助函数一致;
- 按“左左→右右、左右→右左”的镜像顺序入队下一层节点,保证对比顺序正确;
- 返回结果:队列遍历完成后,说明所有镜像节点都对称,返回
true。
性能说明
- 时间复杂度:O(n)(每个节点仅入队/出队一次);
- 空间复杂度:O(n)(最坏情况队列存储一层所有镜像节点对,如完全二叉树的最后一层);
- 优势:非递归实现,避免递归栈溢出风险(如树高度极大时);
- 劣势:需要额外的队列空间,代码量略多于递归法。
解法2:迭代法(深度优先搜索 DFS / 栈模拟)
核心方法:栈模拟递归过程,用栈存储镜像位置的节点对,按“根→右→左”的镜像顺序入栈,逐节点校验结构和值是否对称,逻辑与递归法等价但无递归栈开销。
代码实现
import java.util.Stack;
public boolean isSymmetric(TreeNode root) {
if (root == null) {
return true;
}
// 栈存储镜像位置的节点对
Stack<TreeNode> stack = new Stack<>();
stack.push(root.left);
stack.push(root.right);
while (!stack.isEmpty()) {
// 取出一对镜像节点(栈后进先出,先取right再取left)
TreeNode right = stack.pop();
TreeNode left = stack.pop();
// 双空节点,跳过
if (left == null && right == null) {
continue;
}
// 结构/值不对称
if (left == null || right == null || left.val != right.val) {
return false;
}
// 按镜像顺序入栈下一层节点(保证左左↔右右、左右↔右左的顺序)
stack.push(left.left);
stack.push(right.right);
stack.push(left.right);
stack.push(right.left);
}
return true;
}
核心逻辑说明
- 栈初始化:将根节点的左右子节点作为第一对镜像节点入栈;
- 栈遍历镜像对比:
- 弹出一对节点(注意栈后进先出,先弹right再弹left);
- 校验逻辑与递归法一致;
- 按“左左→右右、左右→右左”的镜像顺序入栈,保证深层节点的对比顺序正确;
- 返回结果:栈遍历完成后返回
true。
性能说明
- 时间复杂度:O(n)(每个节点仅入栈/出栈一次);
- 空间复杂度:O(h)(h为树的高度,栈的深度等于树的高度);
- 优势:非递归实现,可控性更高,避免递归栈溢出;
- 劣势:代码量多于递归法,需要手动管理栈的入栈/出栈顺序。
解法3:递归代码优化(精简基线条件)
核心方法:在原递归逻辑基础上,精简基线条件的写法,代码更简洁但逻辑等价,可读性更高。
代码实现
public boolean isSymmetric(TreeNode root) {
return root == null || isCompare(root.left, root.right);
}
private boolean isCompare(TreeNode left, TreeNode right) {
// 合并结构不对称的基线条件
if (left == null || right == null) {
return left == right;
}
// 值不对称直接返回false
if (left.val != right.val) {
return false;
}
// 递归校验镜像节点
return isCompare(left.left, right.right) && isCompare(left.right, right.left);
}
优势说明
- 逻辑等价:与原递归解法完全一致,仅代码写法优化;
- 代码更精简:将“双空、一空一非空”的判断合并为
left == right,减少冗余代码; - 可读性更高:核心逻辑更突出,新手易理解。
总结
- 递归DFS法(第一次解答):O(n)时间+O(h)空间,逻辑清晰、镜像对比精准,是判断对称二叉树的最优解法,工程中优先使用;
- 迭代BFS法(层序遍历):O(n)时间+O(n)空间,非递归实现,满足进阶要求,适合避免递归栈的场景;
- 迭代DFS法(栈模拟):O(n)时间+O(h)空间,与递归法空间复杂度一致,代码稍复杂;
- 递归精简版:代码更简洁,逻辑等价,可读性更高;
- 关键技巧:
- 核心思想:对称二叉树的本质是“左右子树镜像对称”,需对比
left.left↔right.right、left.right↔right.left; - 基线条件:优先判断“结构对称”(双空/一空一非空),再判断“值对称”;
- 方法选择:优先选递归法(代码简洁),树高度极大时选迭代法(避免栈溢出)。
- 核心思想:对称二叉树的本质是“左右子树镜像对称”,需对比