LeetCode 101. 对称二叉树:两种解法(递归+迭代)详解

LeetCode 上的经典二叉树题目------101. 对称二叉树,这道题核心考察二叉树的遍历逻辑,也是面试中常考的基础题,今天就带大家用「递归」和「迭代」两种思路彻底搞定它,还会拆解代码里的关键细节,帮大家避开易错点。

一、题目解读

题目很简洁:给你一个二叉树的根节点 root,检查它是否轴对称。

先明确「轴对称」的定义:二叉树的左子树和右子树,沿着根节点所在的竖直线镜像对称。简单说就是,左子树的左节点 = 右子树的右节点,左子树的右节点 = 右子树的左节点,且所有对应节点的值都相等,空节点的位置也要对应。

举个例子:

  • 对称的情况:根节点的左孩子和右孩子值相等,左孩子的左孩子 = 右孩子的右孩子,左孩子的右孩子 = 右孩子的左孩子(哪怕都是空节点也符合)。

  • 不对称的情况:任意一对对应节点的值不相等,或空节点位置不对应(比如左子树有左节点,右子树对应位置没有右节点)。

二、前置准备:TreeNode 类定义

题目已经给出了二叉树节点的类定义,这里再贴一次,方便大家对照代码理解(TypeScript 版本):

typescript 复制代码
class TreeNode {
  val: number
  left: TreeNode | null
  right: TreeNode | null
  constructor(val?: number, left?: TreeNode | null, right?: TreeNode | null) {
    this.val = (val === undefined ? 0 : val)  // 节点值默认0
    this.left = (left === undefined ? null : left)  // 左孩子默认null
    this.right = (right === undefined ? null : right)  // 右孩子默认null
  }
}

三、解法一:递归(简洁高效,优先掌握)

3.1 递归思路

递归的核心是「拆分问题」:判断整棵树是否对称,等价于判断「根节点的左子树」和「根节点的右子树」是否镜像对称。

而判断两个子树是否镜像对称,需要满足 3 个条件:

  1. 两个子树的根节点值相等(如果都为空,也符合条件);

  2. 第一个子树的左孩子,和第二个子树的右孩子镜像对称;

  3. 第一个子树的右孩子,和第二个子树的左孩子镜像对称。

基于这个逻辑,我们可以写一个辅助函数,专门判断两个节点是否是镜像关系,再用这个辅助函数递归判断左、右子树。

3.2 递归代码实现

typescript 复制代码
// 辅助函数:判断两个节点是否镜像对称
function isSymmetric_helper(node_1: TreeNode | null, node_2: TreeNode | null): boolean {
  // 情况1:两个节点都为空 → 对称,返回true
  if (!node_1 && !node_2) {
    return true;
  }
  // 情况2:一个为空、一个不为空,或两个节点值不相等 → 不对称,返回false
  if (!node_1 || !node_2 || node_1.val !== node_2.val) {
    return false;
  }
  // 情况3:两个节点值相等,递归判断它们的子节点(镜像对应)
  return isSymmetric_helper(node_1.left, node_2.right) && isSymmetric_helper(node_1.right, node_2.left);
}

// 主函数:判断整棵树是否对称
function isSymmetric_1(root: TreeNode | null): boolean {
  // 边界条件:根节点为空 → 树对称(空树也是对称的)
  if (!root) {
    return true;
  }
  // 递归判断左子树和右子树是否镜像对称
  return isSymmetric_helper(root.left, root.right);
};

3.3 关键细节(易错点)

  • 边界处理:空树(root 为 null)直接返回 true,因为题目中没有说空树不对称;

  • 辅助函数的终止条件:先判断「两个节点都为空」,再判断「一个空一个非空」,避免空指针异常(如果先判断值相等,空节点会报错);

  • 递归传递的节点:必须是「node1.left 和 node2.right」「node1.right 和 node2.left」,这是镜像对称的核心,不能传反。

3.4 复杂度分析

时间复杂度:O(n),n 是二叉树的节点数,每个节点会被访问一次(递归一次);

空间复杂度:O(n),最坏情况是二叉树为链表(比如所有节点都在左子树),递归栈的深度会达到 n。

四、解法二:迭代(用队列实现,规避递归栈溢出)

4.1 迭代思路

迭代的思路和递归一致,都是判断「左子树和右子树是否镜像对称」,只是把递归换成了「队列遍历」。

核心逻辑:用队列存储「需要成对判断的节点」,每次从队列中取出两个节点,判断它们是否对称;然后将这两个节点的「镜像子节点」(node1.left 和 node2.right、node1.right 和 node2.left)依次加入队列,循环这个过程,直到队列为空(全部对称)或找到不对称的节点(直接返回 false)。

4.2 迭代代码实现

typescript 复制代码
function isSymmetric_2(root: TreeNode | null): boolean {
  // 边界条件:空树直接返回true
  if (!root) {
    return true;
  }
  // 队列:存储需要成对判断的节点,初始加入左、右子树的根节点
  let queue = [root.left, root.right];

  // 队列不为空,继续判断
  while (queue.length) {
    const size = queue.length;  // 每次处理队列中所有成对的节点(一层的节点)
    for (let i = 0; i < size; i++) {
      // 每次取出两个节点(成对判断)
      const node_1 = queue.shift();  // 取出第一个节点
      const node_2 = queue.shift();  // 取出第二个节点(与第一个节点镜像对应)

      // 两个节点都为空,继续判断下一对
      if (!node_1 && !node_2) {
        continue;
      }
      // 一个空、一个非空,或值不相等 → 不对称
      if (!node_1 || !node_2 || node_1.val !== node_2.val) {
        return false;
      }
      // 将镜像子节点加入队列,供下一轮判断
      queue.push(node_1.left);
      queue.push(node_2.right);
      queue.push(node_1.right);
      queue.push(node_2.left);
    }
  }

  // 队列为空,所有节点都对称
  return true;
};

3.3 关键细节(易错点)

  • 队列初始化:必须同时加入 root.left 和 root.right,不能只加一个;

  • shift() 方法:队列是「先进先出」,用 shift() 取出队首元素(注意:shift() 会修改原队列,时间复杂度是 O(n),如果想优化,可以用双端队列 deque);

  • 子节点入队顺序:必须是「node1.left、node2.right、node1.right、node2.left」,保证下一轮取出的两个节点依然是镜像对应关系;

  • 循环终止条件:队列为空时,说明所有成对节点都判断完毕,返回 true;只要找到一对不对称节点,直接返回 false,提前终止。

4.4 复杂度分析

时间复杂度:O(n),每个节点会被加入队列一次、取出一次,总共访问 n 次;

空间复杂度:O(n),最坏情况是二叉树为满二叉树,队列中最多会存储 n/2 个节点(最后一层的节点数),整体空间复杂度为 O(n)。

五、两种解法对比 & 总结

解法 优点 缺点 适用场景
递归 代码简洁、思路直观,容易上手 递归栈深度有限制,极端情况下(链表式二叉树)会栈溢出 二叉树深度不大,追求代码简洁
迭代 规避栈溢出问题,稳定性更高 代码稍长,需要手动维护队列 二叉树深度较大,追求稳定性

核心总结

  1. 对称二叉树的核心是「左子树与右子树镜像对称」,本质是「成对节点的对比」;

  2. 递归和迭代的思路一致,都是围绕「成对判断节点」展开,只是实现方式不同;

  3. 无论哪种解法,都要注意「空节点的处理」,这是最容易出错的地方;

  4. 面试中如果被问到这道题,建议先讲递归思路(简洁),再补充迭代思路(考虑边界情况),体现思维的完整性。

六、整体总结

本文围绕 LeetCode 101. 对称二叉树,详细拆解了「递归」和「迭代」两种核心解法,核心目标是帮助大家掌握二叉树镜像对称的判断逻辑,同时规避解题中的常见易错点,适配笔试和面试场景。

从解题逻辑来看,两种解法的核心一致------判断二叉树的左、右子树是否镜像对称,本质都是通过「成对对比对应节点」验证对称性,区别仅在于遍历节点的方式:递归依赖系统栈,代码更简洁;迭代依赖手动维护的队列,避免了递归栈溢出的风险,两种解法可根据二叉树的深度灵活选择。

解题关键提醒:无论使用哪种方法,都要优先处理空节点的边界情况,先判断「两个节点均为空」,再判断「一个空一个非空」,最后对比节点值,这样能有效避免空指针异常,这也是本题最容易失分的地方。

延伸思考:本题的解题思路可迁移到「判断两个二叉树是否镜像」「判断二叉树是否为回文结构」等类似题目中,核心都是「镜像对应节点的成对对比」,掌握本文的两种解法,能轻松应对这类二叉树拓展题目。

相关推荐
ADDDDDD_Trouvaille1 小时前
2026.2.18——OJ86-88题
c++·算法
码云数智-大飞1 小时前
微前端架构落地实战:qiankun vs Module Federation 2026 深度对比与选型指南
前端·架构
IT枫斗者1 小时前
MyBatis批量插入性能优化:从5分钟到3秒的工程化实践
前端·vue.js·mysql·mongodb·性能优化·mybatis
努力学算法的蒟蒻2 小时前
day89(2.18)——leetcode面试经典150
算法·leetcode·面试
丰海洋2 小时前
Leetcode-hot100-283.移动零
算法·leetcode·职场和发展
s_w.h2 小时前
【 C++ 】搜索二叉树
java·开发语言·c++·算法
前端 贾公子2 小时前
深入理解 Vue3 的 v-model 及自定义指令的实现原理(中)
前端·html
俩娃妈教编程2 小时前
2023 年 09 月 二级真题(2)--数字黑洞
c++·算法·while
星火开发设计2 小时前
关联式容器:map 与 multimap 的键值对存储
java·开发语言·数据结构·c++·算法