226. 翻转二叉树 (Easy)

专题归类: 05-二叉树 LeetCode 链接: https://leetcode.cn/problems/invert-binary-tree/


在线做题: 写 Python 代码并运行 · Java 跳转 LeetCode

题目描述

给你一棵二叉树的根节点 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

题目详细分析

  • 数据范围含义: 节点数最多 100,值范围 [-100, 100],非常小,递归绝不会栈溢出。
  • 输入输出特征: 输入一个树的根节点,输出翻转后同一个树的根节点(原地修改,非新建树)。
  • 边界条件: 空树直接返回 None;单节点无需翻转。
  • 核心约束: 必须对每个节点都执行左右子树交换,叶子节点交换两个 None 不影响。
  • 隐藏条件: 这题的出名是因为 Homebrew 作者在 Google 面试时没写出来。本质上考察的是二叉树递归的基础操作。

小白版直白理解

就像照镜子——你举起左手,镜子里的人举起右手。翻转二叉树就是给整棵树照一次镜子:每个节点的左边变成右边,右边变成左边。从最底下的叶子开始,一层一层往上交换,整棵树就被镜像翻转了。


解题思路

思路一:前序递归(推荐)

核心思想: 先交换当前节点的左右孩子,再递归地去翻转左右子树。

为什么用前序? 前序位置是在「进入一个节点后、处理子树之前」执行操作。对于翻转问题,先交换当前节点的左右孩子,然后递归处理它们——这非常符合直觉:先交换,再处理。

def invertTree(root):
    if not root:
        return None
    # 前序位置:先交换当前节点的左右子树
    root.left, root.right = root.right, root.left
    # 递归翻转左右子树
    invertTree(root.left)
    invertTree(root.right)
    return root

思路二:后序递归

核心思想: 先递归翻转左右子树,然后再交换当前节点的左右孩子。

为什么后序也可以? 后序是先处理完子树再回到当前节点。你先确保左右子树各自已经被翻转好了,再交换它们——左右子树内部已经镜像,交换后整棵树就镜像了。

def invertTree(root):
    if not root:
        return None
    # 先递归翻转左右子树
    left = invertTree(root.left)
    right = invertTree(root.right)
    # 后序位置:交换翻转好的左右子树
    root.left = right
    root.right = left
    return root

思路三:BFS 迭代

用队列层序遍历,每遇到一个节点就交换它的左右孩子。

适用场景: 不想用递归时,用 BFS 迭代同样可以完成任务。

from collections import deque
 
def invertTree(root):
    if not root:
        return None
    q = deque([root])
    while q:
        node = q.popleft()
        # 交换当前节点的左右孩子
        node.left, node.right = node.right, node.left
        if node.left:
            q.append(node.left)
        if node.right:
            q.append(node.right)
    return root

易错点

  • 交换的是子树,不是值: 需要交换 root.leftroot.right(让整个子树换边),而不是交换 root.left.valroot.right.val
  • 前序递归不需要保存返回值: invertTree(root.left) 的返回值不需要赋值给任何变量,因为翻转是在原地进行的。
  • 后序递归的赋值顺序: 如果先把左子树的翻转结果赋给 root.left,再处理右子树,要注意 root.left 已经被修改了。正确做法:先保存左右,或者后序时在交换时用临时变量。
  • 空节点处理: if not root: return None 是必须的 base case。

框架提炼

二叉树递归修改结构模板: 无论是翻转、展开、还是重建,都可以用「前序做操作+递归处理子树」或「递归处理子树+后序做操作」两种模式。

前序模式:操作当前节点 → 递归左右子树
后序模式:递归左右子树 → 操作当前节点

关键区别:前序是「自上而下」的指令式操作,后序是「自下而上」的归并式操作。


关联题目