力扣解题-226. 翻转二叉树

30 阅读7分钟

力扣解题-226. 翻转二叉树

给你一棵二叉树的根节点 root ,翻转这棵二叉树,并返回其根节点。

示例 1:

image.png

输入:

root = [4,2,7,1,3,6,9]

输出:[4,7,2,9,6,3,1]

示例 2:

image.png

输入:root = [2,1,3]

输出:[2,3,1]

示例 3:

输入:root = []

输出:[]

提示:

树中节点数目范围在 [0, 100] 内

-100 <= Node.val <= 100

Related Topics

树、深度优先搜索、广度优先搜索、二叉树


第一次解答

解题思路

核心方法:递归法(深度优先搜索 DFS),采用“后序遍历”的递归逻辑,先递归翻转左右子树,再交换当前节点的左右子节点,最终实现整棵树的翻转,逻辑简洁且时间/空间复杂度最优。

核心逻辑拆解

翻转二叉树的核心是“自底向上”交换每个节点的左右子节点,递归的本质是将“翻转整棵树”拆解为“翻转左右子树+交换当前节点的左右子节点”:

  1. 基线条件(递归终止):若当前节点root == null(空节点),直接返回null(空树无需翻转);
  2. 递归翻转子树
    • 先递归翻转右子树,并将结果赋值给临时变量temp(实际是先保存原左子节点,再递归处理右子树);
    • 核心操作:root.left = invertTree(root.right)(将当前节点的左子节点替换为翻转后的右子树);
    • root.right = invertTree(temp)(将当前节点的右子节点替换为翻转后的原左子树);
  3. 返回结果:返回当前节点(翻转后的子树根节点),最终根节点返回的就是整棵翻转后的树。
具体步骤(以示例1 root=[4,2,7,1,3,6,9]为例)
递归层级节点操作子树状态变化
41左右子树为空,直接返回11的左右子树无变化
43左右子树为空,直接返回33的左右子树无变化
32交换左右子节点(1↔3)2的左=3,右=1
46左右子树为空,直接返回66的左右子树无变化
49左右子树为空,直接返回99的左右子树无变化
37交换左右子节点(6↔9)7的左=9,右=6
24交换左右子节点(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);
  • 优势:
    1. 代码极简,仅几行逻辑完成核心翻转,符合二叉树的递归特性;
    2. 后序遍历逻辑天然保证“先翻转子树,再交换当前节点”,无逻辑漏洞;
    3. 天然处理空树、单节点树等边界场景。
    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;
}
核心逻辑说明
  1. 边界处理:空树直接返回null;
  2. 队列初始化:根节点入队,开始层序遍历;
  3. 层序翻转
    • 取出队首节点,交换其左右子节点(核心操作);
    • 将交换后的左右子节点(非空时)入队,继续处理下一层;
  4. 返回结果:根节点未改变,返回原根节点(其左右子树已完成翻转)。
性能说明
  • 时间复杂度: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;
}
核心逻辑说明
  1. 栈初始化:根节点入栈,开始深度优先遍历;
  2. 栈遍历翻转
    • 弹出栈顶节点,交换其左右子节点;
    • 按“右→左”顺序入栈子节点(栈后进先出,保证左子节点先被处理,与递归顺序一致);
  3. 返回结果:返回原根节点(已完成翻转)。
性能说明
  • 时间复杂度: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;
}
优势说明
  • 逻辑等价:与原解法的后序递归最终效果一致,仅执行顺序不同;
  • 可读性更高:“先交换当前节点,再处理子树”的逻辑更符合直觉;
  • 性能一致:时间/空间复杂度与原递归解法完全相同。

总结

  1. 后序递归DFS法(第一次解答):O(n)时间+O(h)空间,代码极简、逻辑优雅,是翻转二叉树的最优解法,工程中优先使用;
  2. 迭代BFS法(层序遍历):O(n)时间+O(n)空间,非递归实现,适合需要避免递归栈的场景;
  3. 迭代DFS法(栈模拟):O(n)时间+O(h)空间,与递归法空间复杂度一致,代码稍复杂;
  4. 前序递归法:逻辑更直观,新手易理解,性能与后序递归一致;
  5. 关键技巧:
    • 核心思想:翻转二叉树的本质是“交换每个节点的左右子节点”,递归/迭代仅实现方式不同;
    • 方法选择:优先选递归法(代码简洁),树高度极大时选迭代法(避免栈溢出);
    • 边界处理:空树直接返回null,单节点树交换后无变化,天然覆盖所有边界场景。