目录

题目描述

226. 翻转二叉树

image-20250418231546073

image-20250418231602606

题意分析

给定二叉树的根节点,把整棵树左右镜像后返回根。所谓镜像,是每一个节点的左右孩子都要对调——不只是根节点这一层。以样例 [4,2,7,1,3,6,9] 为例,仅交换根的两个孩子得到的是 [4,7,2,6,9,1,3],而正确答案是 [4,7,2,9,6,3,1]:深层的 1,36,9 也各自换了位置。

题目允许原地修改并返回原根,不要求新建一棵树;节点值本身不动,动的只是左右指针的指向。

规模很小(节点数不超过 100),任何遍历一遍的做法都绰绰有余。边界只有一个:root 可能为空,空树的镜像还是空树。

解法:递归交换左右子树

核心思路

问题关键:镜像不是只交换根节点,而是每个节点都要交换左右孩子。左右子树仍是同一个问题,因此递归最自然。

定义 invertTree(node):返回以 node 为根、已经完全翻转的子树。递归得到翻转后的左右子树,再交叉挂回当前节点;空节点直接返回。由定义可知,两棵子树正确翻转且位置互换后,当前子树也一定正确,递归因此成立。

若树很深、担心递归栈,可改用队列或显式栈遍历;每访问一个节点就交换其左右孩子,核心操作不变。

解题步骤

  • root == null 时返回 null,作为递归出口。
  • 分别递归翻转原左子树和原右子树,并保存两个返回值。
  • 将翻转后的右子树挂到 root.left,翻转后的左子树挂到 root.right
  • 返回 root,供上一层继续连接。

例如节点 2 的孩子是 1、3,递归返回后挂成 3、1;每个节点都做同样的交换,整棵树自然完成镜像。

代码实现

class Solution {
    public TreeNode invertTree(TreeNode root) {
        if (root == null) {
            return null;
        }

        // 左右子树分别翻转后,再交换挂回当前节点。
        TreeNode left = invertTree(root.left);
        TreeNode right = invertTree(root.right);
        root.left = right;
        root.right = left;
        return root;
    }
}
func invertTree(root *TreeNode) *TreeNode {
    if root == nil {
        return nil
    }

    // 递归翻转左右子树,再在当前节点完成镜像交换。
    left := invertTree(root.Left)
    right := invertTree(root.Right)
    root.Left = right
    root.Right = left
    return root
}

复杂度分析

  • 时间复杂度:$O(n)$。每个节点恰好被访问一次,每次只做常数次指针操作。
  • 空间复杂度:$O(h)$。递归栈深度等于树高,平衡时为 $O(\log n)$,链状退化时最坏 $O(n)$。

关键点总结

  • 先明确递归函数的承诺:返回“已经翻转完成”的当前子树。
  • 当前层只负责交叉连接两个递归结果,子树内部交给递归处理。
  • 面试时能补充:递归空间取决于树高;极深树可换成显式栈或队列。

易错点总结

  • 覆盖了原孩子再读取:若先写 root.left = invertTree(root.right),再递归 root.left[4,2,7] 会重复处理原右子树并丢掉节点 2。先保存两个递归结果再赋值。
  • 只交换根节点[4,2,7,1,3,6,9] 会错误得到 [4,7,2,6,9,1,3],深层节点并未镜像。
  • 漏掉空节点出口:空树会触发空指针异常;叶子节点的空孩子也依赖这个出口结束递归。
  • 交换节点值而不是孩子指针:镜像改变的是树结构,只换值无法保证子树位置正确。

相似题目

题目 难度 考察点
剑指 Offer 27. 二叉树的镜像 简单 同一题的另一版本,可练习先交换再递归的写法
101. 对称二叉树 简单 不修改树,双指针同步递归判断镜像相等
951. 翻转等价二叉树 中等 每个节点可选翻或不翻,递归带分支讨论