一、题目描述
给定一棵二叉树的根节点 root,请你翻转这棵二叉树,并返回翻转后的根节点。
所谓翻转二叉树,就是交换每个节点的左右子树。
示例:
输入:
4
/ \
2 7
/ \ / \
1 3 6 9
输出:
4
/ \
7 2
/ \ / \
9 6 3 1
二、为什么这题值得学习
LeetCode 226 是二叉树最经典的入门题之一,也是很多公司喜欢考察的 DFS 基础题。
虽然代码只有几行,但它能够帮助我们理解:
- 二叉树递归思想
- DFS(深度优先搜索)
- 前序、后序遍历的应用
- BFS(层序遍历)
这道题最大的价值不是交换左右节点,而是理解递归到底是在做什么。
三、核心思想
翻转二叉树其实就是:
遍历整棵树,把每一个节点的左右孩子交换即可。
例如:
原树:
A
/ \
B C
交换后:
A
/ \
C B
对于每一个节点都执行同样的操作,因此天然适合递归。
四、解法一:递归(推荐)
思路
对于当前节点:
- 交换左右孩子;
- 翻转左子树;
- 翻转右子树。
递归结束条件:
root == null
直接返回。
Java 实现
class Solution {
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;
}
}
执行过程
例如:
4
/ \
2 7
第一步:
交换:
4
/ \
7 2
继续递归:
翻转7
↓
翻转2
直到所有节点完成交换。
复杂度分析
| 指标 | 数值 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(h)(递归栈) |
其中:
- n 为节点数量
- h 为树高
五、解法二:DFS(显式栈)
递归其实就是系统帮我们维护了一个栈。
我们也可以自己维护一个栈。
思路
- 根节点入栈;
- 弹出节点;
- 交换左右孩子;
- 左右孩子继续入栈。
直到栈为空。
Java 实现
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
Deque<TreeNode> stack = new ArrayDeque<>();
stack.push(root);
while (!stack.isEmpty()) {
TreeNode node = stack.pop();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left != null) {
stack.push(node.left);
}
if (node.right != null) {
stack.push(node.right);
}
}
return root;
}
}
复杂度分析
| 指标 | 数值 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(h) |
六、解法三:BFS(层序遍历)
除了 DFS,还可以按层遍历整棵树。
利用队列:
每访问一个节点:
直接交换左右孩子。
思路
根节点入队
↓
弹出节点
↓
交换左右孩子
↓
左右孩子入队
↓
直到队列为空
Java 实现
class Solution {
public TreeNode invertTree(TreeNode root) {
if (root == null) {
return null;
}
Queue<TreeNode> queue = new ArrayDeque<>();
queue.offer(root);
while (!queue.isEmpty()) {
TreeNode node = queue.poll();
TreeNode temp = node.left;
node.left = node.right;
node.right = temp;
if (node.left != null) {
queue.offer(node.left);
}
if (node.right != null) {
queue.offer(node.right);
}
}
return root;
}
}
复杂度分析
| 指标 | 数值 |
|---|---|
| 时间复杂度 | O(n) |
| 空间复杂度 | O(n) |
最坏情况下,队列可能保存整层节点。
七、三种解法对比
| 解法 | 推荐程度 | 时间复杂度 | 空间复杂度 | 面试评价 |
|---|---|---|---|---|
| 递归 | ⭐⭐⭐⭐⭐ | O(n) | O(h) | 最常用、代码最简洁 |
| DFS(栈) | ⭐⭐⭐⭐ | O(n) | O(h) | 展示递归转迭代能力 |
| BFS(队列) | ⭐⭐⭐⭐ | O(n) | O(n) | 展示层序遍历思想 |
八、面试高频追问
❓ 为什么递归能够解决?
因为每个节点执行的操作完全一致:
交换左右子树。
每棵子树又是一棵完整的二叉树,因此天然具有递归结构。
❓ 为什么可以前序也可以后序?
因为:
交换左右孩子不会影响最终结果。
所以:
交换
↓
递归左右
或者:
递归左右
↓
交换
最终得到的树完全一样。
❓ 时间复杂度是多少?
每个节点只访问一次。
所以:
O(n)
❓ 空间复杂度是多少?
递归:
O(h)
h 为树高。
如果:
树退化成链表。
最坏:
O(n)
九、常见错误
1. 忘记交换
很多人写成:
invertTree(root.left);
invertTree(root.right);
结果:
没有交换。
树不会发生变化。
2. 空指针
忘记:
if(root==null)
会:
root.left
空指针异常。
3. 交换顺序写错
正确:
TreeNode temp = root.left;
root.left = root.right;
root.right = temp;
不要直接:
root.left = root.right;
root.right = root.left;
否则:
左右孩子都会变成同一个节点。
十、一句话总结
翻转二叉树的本质就是遍历整棵树,并交换每个节点的左右子树 。递归实现最简单,也是面试最推荐的写法;DFS 和 BFS 本质上都是对递归思想的迭代实现,三种方法时间复杂度均为 O(n)。