力扣解题-226. 翻转二叉树
给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。
示例 1:
输入:
root = [4,2,7,1,3,6,9]
输出:[4,7,2,9,6,3,1]
示例 2:
输入:root = [2,1,3]
输出:[2,3,1]
示例 3:
输入:root = []
输出:[]
提示:
树中节点数目范围在 [0, 100] 内
-100 <= Node.val <= 100
Related Topics
树、深度优先搜索、广度优先搜索、二叉树
第一次解答
解题思路
核心方法:递归法(深度优先搜索 DFS),采用“后序遍历”的递归逻辑,先递归翻转左右子树,再交换当前节点的左右子节点,最终实现整棵树的翻转,逻辑简洁且时间/空间复杂度最优。
核心逻辑拆解
翻转二叉树的核心是“自底向上”交换每个节点的左右子节点,递归的本质是将“翻转整棵树”拆解为“翻转左右子树+交换当前节点的左右子节点”:
- 基线条件(递归终止):若当前节点
root == null(空节点),直接返回null(空树无需翻转); - 递归翻转子树:
- 先递归翻转右子树,并将结果赋值给临时变量
temp(实际是先保存原左子节点,再递归处理右子树); - 核心操作:
root.left = invertTree(root.right)(将当前节点的左子节点替换为翻转后的右子树); root.right = invertTree(temp)(将当前节点的右子节点替换为翻转后的原左子树);
- 先递归翻转右子树,并将结果赋值给临时变量
- 返回结果:返回当前节点(翻转后的子树根节点),最终根节点返回的就是整棵翻转后的树。
具体步骤(以示例1 root=[4,2,7,1,3,6,9]为例)
| 递归层级 | 节点 | 操作 | 子树状态变化 |
|---|---|---|---|
| 4 | 1 | 左右子树为空,直接返回1 | 1的左右子树无变化 |
| 4 | 3 | 左右子树为空,直接返回3 | 3的左右子树无变化 |
| 3 | 2 | 交换左右子节点(1↔3) | 2的左=3,右=1 |
| 4 | 6 | 左右子树为空,直接返回6 | 6的左右子树无变化 |
| 4 | 9 | 左右子树为空,直接返回9 | 9的左右子树无变化 |
| 3 | 7 | 交换左右子节点(6↔9) | 7的左=9,右=6 |
| 2 | 4 | 交换左右子节点(2↔7) | 4的左=7,右=2 |
| 最终树结构:4→7→2→9→6→3→1,与示例一致。 |
性能说明
- 时间复杂度:O(n)(每个节点仅被访问一次,n为节点总数);
- 空间复杂度:O(h)(h为树的高度,递归调用栈的深度等于树的高度):
- 最好情况(平衡二叉树):h = log₂n,空间复杂度O(logn);
- 最坏情况(斜树):h = n,空间复杂度O(n);
- 优势:
- 代码极简,仅几行逻辑完成核心翻转,符合二叉树的递归特性;
- 后序遍历逻辑天然保证“先翻转子树,再交换当前节点”,无逻辑漏洞;
- 天然处理空树、单节点树等边界场景。
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
TreeNode temp = root.left;
root.left = invertTree(root.right);
root.right = invertTree(temp);
return root;
}
示例解答
解题思路
解法1:迭代法(广度优先搜索 BFS / 层序遍历)
核心方法:队列辅助层序翻转,利用队列实现二叉树的层序遍历,逐节点交换左右子节点,属于“自顶向下”的迭代实现,避免递归栈的调用,适合树高度较大的场景。
代码实现
import java.util.LinkedList;
import java.util.Queue;
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
// 队列存储待处理的节点
Queue<TreeNode> queue = new LinkedList<>();
queue.offer(root);
while (!queue.isEmpty()) {
// 取出当前节点
TreeNode curr = queue.poll();
// 交换当前节点的左右子节点
TreeNode temp = curr.left;
curr.left = curr.right;
curr.right = temp;
// 左子节点入队(不为空时)
if (curr.left != null) {
queue.offer(curr.left);
}
// 右子节点入队(不为空时)
if (curr.right != null) {
queue.offer(curr.right);
}
}
return root;
}
核心逻辑说明
- 边界处理:空树直接返回null;
- 队列初始化:根节点入队,开始层序遍历;
- 层序翻转:
- 取出队首节点,交换其左右子节点(核心操作);
- 将交换后的左右子节点(非空时)入队,继续处理下一层;
- 返回结果:根节点未改变,返回原根节点(其左右子树已完成翻转)。
性能说明
- 时间复杂度:O(n)(每个节点仅入队/出队一次,交换操作O(1));
- 空间复杂度:O(n)(最坏情况队列存储一层所有节点,如完全二叉树的最后一层有n/2个节点);
- 优势:非递归实现,避免递归栈溢出风险(如树高度极大时);
- 劣势:需要额外的队列空间,代码量略多于递归法。
解法2:迭代法(深度优先搜索 DFS / 栈模拟)
核心方法:栈模拟递归过程,用栈存储待处理的节点,按“根→右→左”的顺序入栈(模拟递归的深度优先遍历),逐节点交换左右子节点,逻辑与递归法等价但无递归栈开销。
代码实现
import java.util.Stack;
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
// 栈存储待处理的节点
Stack<TreeNode> stack = new Stack<>();
stack.push(root);
while (!stack.isEmpty()) {
// 取出当前节点
TreeNode curr = stack.pop();
// 交换左右子节点
TreeNode temp = curr.left;
curr.left = curr.right;
curr.right = temp;
// 右子节点先入栈(栈后进先出,保证左子节点先处理)
if (curr.right != null) {
stack.push(curr.right);
}
// 左子节点后入栈
if (curr.left != null) {
stack.push(curr.left);
}
}
return root;
}
核心逻辑说明
- 栈初始化:根节点入栈,开始深度优先遍历;
- 栈遍历翻转:
- 弹出栈顶节点,交换其左右子节点;
- 按“右→左”顺序入栈子节点(栈后进先出,保证左子节点先被处理,与递归顺序一致);
- 返回结果:返回原根节点(已完成翻转)。
性能说明
- 时间复杂度:O(n)(每个节点仅入栈/出栈一次);
- 空间复杂度:O(h)(h为树的高度,栈的深度等于树的高度);
- 优势:非递归实现,可控性更高,避免递归栈溢出;
- 劣势:代码量多于递归法,需要手动管理栈的入栈/出栈顺序。
解法3:前序递归优化(更直观的递归写法)
核心方法:前序遍历递归,先交换当前节点的左右子节点,再递归翻转子树,逻辑与原解法一致,但写法更直观,新手易理解。
代码实现
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
// 先交换当前节点的左右子节点(前序)
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
// 再递归翻转左右子树
invertTree(root.left);
invertTree(root.right);
return root;
}
优势说明
- 逻辑等价:与原解法的后序递归最终效果一致,仅执行顺序不同;
- 可读性更高:“先交换当前节点,再处理子树”的逻辑更符合直觉;
- 性能一致:时间/空间复杂度与原递归解法完全相同。
总结
- 后序递归DFS法(第一次解答):O(n)时间+O(h)空间,代码极简、逻辑优雅,是翻转二叉树的最优解法,工程中优先使用;
- 迭代BFS法(层序遍历):O(n)时间+O(n)空间,非递归实现,适合需要避免递归栈的场景;
- 迭代DFS法(栈模拟):O(n)时间+O(h)空间,与递归法空间复杂度一致,代码稍复杂;
- 前序递归法:逻辑更直观,新手易理解,性能与后序递归一致;
- 关键技巧:
- 核心思想:翻转二叉树的本质是“交换每个节点的左右子节点”,递归/迭代仅实现方式不同;
- 方法选择:优先选递归法(代码简洁),树高度极大时选迭代法(避免栈溢出);
- 边界处理:空树直接返回null,单节点树交换后无变化,天然覆盖所有边界场景。