LeetCode 226. 翻转二叉树:两种解法(递归+迭代)详解

LeetCode简单题------翻转二叉树,这道题看似简单,却能帮我们快速掌握二叉树的递归和迭代遍历思路,非常适合入门练习。今天就带大家一步步拆解题目,详解两种常用解法,附上完整代码和易错点提醒,新手也能轻松看懂!

一、题目解读

题目很直白:给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

什么是翻转二叉树?其实就是把每一个节点的「左子树」和「右子树」互换。举个简单例子:如果根节点有左孩子3、右孩子9,翻转后就变成左孩子9、右孩子3;同理,每个子节点也需要做同样的操作,直到所有节点都完成左右互换。

先看题目给出的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
  }
}

二、核心思路

翻转二叉树的核心逻辑很简单:遍历每一个节点,将该节点的左右子节点互换

关键在于「如何遍历所有节点」------ 二叉树的遍历主要有两种方式:递归遍历(深度优先DFS)和迭代遍历(广度优先BFS,常用队列实现)。对应这两种遍历方式,我们可以写出两种解法,下面分别详细讲解。

三、解法一:递归实现(深度优先DFS)

1. 思路拆解

递归的核心是「自顶向下」处理节点,遵循「终止条件→处理当前节点→递归处理子节点」的逻辑:

  1. 终止条件:如果当前节点为null(空树或叶子节点的子节点),直接返回null,无需翻转;

  2. 处理当前节点:用一个临时变量temp,保存当前节点的左子节点,然后将左子节点替换为右子节点,右子节点替换为temp(完成左右互换);

  3. 递归处理:分别递归翻转当前节点的左子树和右子树;

  4. 返回结果:返回处理后的当前节点(作为父节点的子节点)。

2. 完整代码

typescript 复制代码
function invertTree_1(root: TreeNode | null): TreeNode | null {
  // 终止条件:当前节点为空,直接返回null
  if (root === null) {
    return null;
  }
  // 互换当前节点的左右子节点
  const temp = root.left;
  root.left = root.right;
  root.right = temp;
  // 递归翻转左子树和右子树
  invertTree_1(root.left);
  invertTree_1(root.right);
  // 返回翻转后的当前节点
  return root;
};

3. 关键解析

为什么递归能生效?因为递归会逐层深入到二叉树的最底层(叶子节点),然后逐层回溯,确保每一个节点的左右子节点都被互换。

举个例子:一棵三层二叉树(根1,左2、右3;2的左4、右5),递归过程是:

  1. 处理根1,互换左2和右3 → 根1的左是3、右是2;

  2. 递归处理根1的左子节点3(3是叶子节点,互换后还是3);

  3. 递归处理根1的右子节点2,互换2的左4和右5 → 2的左是5、右是4;

  4. 递归处理2的左子节点5(叶子节点)、右子节点4(叶子节点);

  5. 回溯返回,最终得到翻转后的二叉树。

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

空间复杂度:O(h),h是二叉树的高度,递归调用栈的深度等于树的高度(最坏情况是斜树,h=n,空间复杂度O(n))。

四、解法二:迭代实现(广度优先BFS)

递归虽然简洁,但如果树的高度很高,可能会出现栈溢出的问题(比如斜树,递归深度达到n)。这时可以用迭代的方式(广度优先),用队列来存储待处理的节点,避免递归栈溢出。

1. 思路拆解

广度优先的核心是「逐层处理」,用队列实现"先进先出",确保每一层的节点都被处理完再处理下一层:

  1. 终止条件(边界处理):如果根节点为null,直接返回null;

  2. 初始化队列:将根节点加入队列,队列中存储的是「待互换左右子节点的节点」;

  3. 循环处理:只要队列不为空,就取出队列头部的节点,互换其左右子节点;

  4. 入队操作:将互换后的左子节点、右子节点(如果不为null)加入队列,等待后续处理;

  5. 循环结束后,返回根节点(此时整棵树已翻转完成)。

2. 完整代码

typescript 复制代码
function invertTree_2(root: TreeNode | null): TreeNode | null {
  // 边界处理:根节点为空,直接返回null
  if (root === null) {
    return null;
  }
  // 初始化队列,将根节点加入队列
  const queue: TreeNode[] = [root];
  // 队列不为空,继续处理
  while (queue.length > 0) {
    // 取出队列头部的节点(先进先出)
    const node = queue.shift();
    // 防止queue.shift()返回undefined(实际不会触发,因队列长度>0时才取)
    if (node === undefined) {
      return null;
    }
    // 互换当前节点的左右子节点
    const temp = node.left;
    node.left = node.right;
    node.right = temp;
    // 左子节点不为null,加入队列,等待处理
    if (node.left !== null) {
      queue.push(node.left);
    }
    // 右子节点不为null,加入队列,等待处理
    if (node.right !== null) {
      queue.push(node.right);
    }
  }
  // 所有节点处理完毕,返回翻转后的根节点
  return root;
};

3. 关键解析

队列的作用是"缓存"待处理的节点,确保我们能逐层处理每一个节点。还是用上面的三层二叉树举例,迭代过程是:

  1. 队列初始化:[1],取出1,互换左右→1的左3、右2;将3、2加入队列→队列变为[3,2];

  2. 取出3,互换左右(3是叶子节点,无变化);3的左右都为null,不加入队列→队列变为[2];

  3. 取出2,互换左右→2的左5、右4;将5、4加入队列→队列变为[5,4];

  4. 取出5,互换左右(无变化);不加入队列→队列变为[4];

  5. 取出4,互换左右(无变化);不加入队列→队列变为空,循环结束;

  6. 返回根节点1,翻转完成。

时间复杂度:O(n),每个节点只被取出队列一次,处理一次;

空间复杂度:O(n),队列中最多存储一层的节点数,最坏情况是完全二叉树的最后一层,节点数约为n/2,所以空间复杂度O(n)。

五、两种解法对比

解法 核心思想 时间复杂度 空间复杂度 适用场景
递归(DFS) 自顶向下,递归遍历子树 O(n) O(h)(h为树高) 树的高度较低,代码简洁易写
迭代(BFS) 逐层处理,队列缓存节点 O(n) O(n) 树的高度很高(避免递归栈溢出)

六、易错点提醒

  1. 边界处理:一定要先判断root是否为null,否则会出现"访问null的left/right"的错误;

  2. 临时变量:互换左右子节点时,必须用临时变量保存,不能直接写root.left = root.right、root.right = root.left(这样会导致右子节点被覆盖,最终左右子节点相同);

  3. 队列操作:迭代解法中,queue.shift()会取出队列头部元素,若队列长度为0,shift()返回undefined,所以加了一层判断(实际不会触发,但能避免语法报错);

  4. 递归终止:递归解法中,处理完当前节点后,必须递归调用左右子树,否则只有根节点的左右子节点被互换,子树不会翻转。

七、总结

翻转二叉树是二叉树操作的入门题,核心就是「遍历节点+左右互换」。两种解法各有优势:递归写法简洁,适合日常刷题;迭代写法更稳定,适合处理极端场景(高树)。

通过这道题,我们可以巩固二叉树的递归和广度优先遍历思路,后续遇到二叉树的修改、遍历类题目,都可以沿用类似的逻辑。

相关推荐
大熊背1 小时前
APEX系统中为什么 不用与EV0的差值计算曝光参数调整量
人工智能·算法·apex·自动曝光
REDcker1 小时前
HDR Vivid 技术介绍
数据库·算法·视频·sdr·屏幕·显示技术·dhr
哆啦A梦15881 小时前
Vue3魔法手册 作者 张天禹 013_pinia
前端·vue.js·typescript
ab1515171 小时前
2.18完成109、112、113
算法
哆啦A梦15881 小时前
Vue3魔法手册 作者 张天禹 014_组件通信
前端·vue.js·typescript
木斯佳2 小时前
前端八股文面经大全:有赞前端一面二面HR面(2026-1-13)·面经深度解析
前端·状态模式
码云数智-园园2 小时前
Vue 3 + TypeScript 企业级项目架构实战:从0到1打造可维护的前端工程体系
前端·vue.js·typescript
CappuccinoRose2 小时前
CSS 语法学习文档(十五)
前端·学习·重构·渲染·浏览器
追随者永远是胜利者2 小时前
(LeetCode-Hot100)64. 最小路径和
java·算法·leetcode·职场和发展·go