Article / 文章

LeetCode 第226题:翻转二叉树

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

题目描述

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

难度

简单

题目链接

点击在LeetCode中查看题目

示例

示例 1:

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

示例1

示例 2:

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

示例 3:

输入:root = []
输出:[]

提示

  • 树中节点数目范围在 [0, 100] 内
  • -100 <= Node.val <= 100

解题思路

这道题要求我们翻转一棵二叉树,也就是将每个节点的左右子树交换位置。这是一个非常经典的二叉树问题,可以使用递归或迭代方法解决。

方法一:递归

递归的思路非常清晰:

  1. 如果当前节点为空,直接返回 null
  2. 递归翻转左子树和右子树。
  3. 交换当前节点的左右子树。
  4. 返回当前节点。

时间复杂度:O(n),其中 n 是树中节点的个数,每个节点只被访问一次。 空间复杂度:O(h),其中 h 是树的高度,递归调用的栈空间取决于树的高度。

方法二:迭代(使用队列)

我们也可以使用层序遍历的方式来翻转二叉树:

  1. 创建一个队列,将根节点入队。
  2. 当队列不为空时,弹出一个节点,交换其左右子树。
  3. 如果左子树不为空,将左子树入队。
  4. 如果右子树不为空,将右子树入队。
  5. 重复步骤2-4,直到队列为空。

时间复杂度:O(n),每个节点只被访问一次。 空间复杂度:O(n),在最坏情况下,队列中可能包含树中所有的节点。

代码实现

C# 实现

/**
 * Definition for a binary tree node.
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left;
 *     public TreeNode right;
 *     public TreeNode(int val=0, TreeNode left=null, TreeNode right=null) {
 *         this.val = val;
 *         this.left = left;
 *         this.right = right;
 *     }
 * }
 */
public 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;
    }
    
    // 方法二:迭代(使用队列)
    public TreeNode InvertTreeIterative(TreeNode root) {
        if (root == null) {
            return null;
        }
        
        Queue<TreeNode> queue = new Queue<TreeNode>();
        queue.Enqueue(root);
        
        while (queue.Count > 0) {
            TreeNode node = queue.Dequeue();
            
            // 交换左右子树
            TreeNode temp = node.left;
            node.left = node.right;
            node.right = temp;
            
            // 将子节点加入队列
            if (node.left != null) {
                queue.Enqueue(node.left);
            }
            if (node.right != null) {
                queue.Enqueue(node.right);
            }
        }
        
        return root;
    }
}

Python 实现

# Definition for a binary tree node.
# class TreeNode:
#     def __init__(self, val=0, left=None, right=None):
#         self.val = val
#         self.left = left
#         self.right = right
class Solution:
    # 方法一:递归
    def invertTree(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        
        # 递归翻转左右子树
        left = self.invertTree(root.left)
        right = self.invertTree(root.right)
        
        # 交换左右子树
        root.left = right
        root.right = left
        
        return root
    
    # 方法二:迭代(使用队列)
    def invertTreeIterative(self, root: TreeNode) -> TreeNode:
        if not root:
            return None
        
        from collections import deque
        queue = deque([root])
        
        while queue:
            node = queue.popleft()
            
            # 交换左右子树
            node.left, node.right = node.right, node.left
            
            # 将子节点加入队列
            if node.left:
                queue.append(node.left)
            if node.right:
                queue.append(node.right)
        
        return root

C++ 实现

/**
 * Definition for a binary tree node.
 * struct TreeNode {
 *     int val;
 *     TreeNode *left;
 *     TreeNode *right;
 *     TreeNode() : val(0), left(nullptr), right(nullptr) {}
 *     TreeNode(int x) : val(x), left(nullptr), right(nullptr) {}
 *     TreeNode(int x, TreeNode *left, TreeNode *right) : val(x), left(left), right(right) {}
 * };
 */
class Solution {
public:
    // 方法一:递归
    TreeNode* invertTree(TreeNode* root) {
        if (root == nullptr) {
            return nullptr;
        }
        
        // 递归翻转左右子树
        TreeNode* left = invertTree(root->left);
        TreeNode* right = invertTree(root->right);
        
        // 交换左右子树
        root->left = right;
        root->right = left;
        
        return root;
    }
    
    // 方法二:迭代(使用队列)
    TreeNode* invertTreeIterative(TreeNode* root) {
        if (root == nullptr) {
            return nullptr;
        }
        
        queue<TreeNode*> q;
        q.push(root);
        
        while (!q.empty()) {
            TreeNode* node = q.front();
            q.pop();
            
            // 交换左右子树
            TreeNode* temp = node->left;
            node->left = node->right;
            node->right = temp;
            
            // 将子节点加入队列
            if (node->left) {
                q.push(node->left);
            }
            if (node->right) {
                q.push(node->right);
            }
        }
        
        return root;
    }
};

性能分析

各语言实现的性能对比:

实现语言 执行用时 内存消耗 说明
C# (递归) 88 ms 38.4 MB 在小型树上效率较高
C# (迭代) 92 ms 39.2 MB 队列操作略微增加了时间和空间开销
Python (递归) 32 ms 16.2 MB Python的递归实现非常简洁
Python (迭代) 36 ms 16.3 MB 队列操作增加了些许开销
C++ (递归) 0 ms 9.4 MB C++实现效率最高
C++ (迭代) 0 ms 9.6 MB 队列操作增加了微小的内存开销

补充说明

代码亮点

  1. 递归实现简洁清晰,体现了二叉树问题的典型解决思路
  2. 迭代实现使用队列进行层序遍历,避免了递归栈溢出的风险
  3. 两种实现都利用了二叉树的结构特性,实现高效翻转

优化方向

  1. 在实际应用中,可以根据树的特性选择合适的遍历方式
  2. 对于高度非常大的树,可以考虑使用迭代方法来避免栈溢出
  3. 可以尝试使用栈进行深度优先遍历,实现另一种迭代解法

解题难点

  1. 理解二叉树的递归结构和翻转操作
  2. 确保递归终止条件正确设置
  3. 在迭代实现中正确处理节点的出队和入队顺序

常见错误

  1. 忘记处理根节点为空的情况
  2. 错误地交换左右子树,导致结构混乱
  3. 在递归实现中返回错误的节点
  4. 在迭代实现中队列操作顺序有误

相关题目