LEETCODE 226Easy
翻转二叉树
翻转整棵树等于交换根的左右孩子,再让两棵子树各自翻转——一个动作递归下去就够了。
问题拆解
翻转的效果是让整棵树沿中轴左右镜像。乍看要动到每一层的每个节点,似乎很繁琐,但只要盯住根节点就会发现:翻转整棵树,无非是把根的左右孩子交换,然后让换过去的两棵子树各自也完成翻转。
这是一个可以原地递归下去的操作——每一层都只做“交换 + 下探”这一件事。
一个看似要动全局的变换,往往能拆成“当前层做一步,剩下的交给子问题”。
交换后递归
public TreeNode invertTree(TreeNode root) {
if (root == null) return null;
TreeNode tmp = root.left;
root.left = root.right;
root.right = tmp;
invertTree(root.left);
invertTree(root.right);
return root;
}
def invertTree(root):
if not root:
return None
root.left, root.right = root.right, root.left
invertTree(root.left)
invertTree(root.right)
return root
func invertTree(root *TreeNode) *TreeNode {
if root == nil {
return nil
}
root.Left, root.Right = root.Right, root.Left
invertTree(root.Left)
invertTree(root.Right)
return root
}
pub fn invert_tree(root: Option<Rc<RefCell<TreeNode>>>) -> Option<Rc<RefCell<TreeNode>>> {
if let Some(node) = &root {
let mut n = node.borrow_mut();
let left = n.left.take();
n.left = n.right.take();
n.right = left;
Self::invert_tree(n.left.clone());
Self::invert_tree(n.right.clone());
}
root
}
Python 的元组赋值让交换一行搞定,不需要临时变量。先交换还是先递归其实都行:因为交换只影响当前节点的两个指针,不改变子树内部结构,两种顺序得到的结果一致。空树直接返回,是递归自然收口的地方。
如果不想用递归栈,也可以借助一个队列做层序遍历,对每个出队的节点交换其左右孩子,再把孩子入队,效果完全相同。
复杂度
| 指标 | 复杂度 | 原因 |
|---|---|---|
| 时间 | O(n) |
每个节点被交换一次 |
| 空间 | O(h) |
递归栈深度等于树高 |
可以迁移的模式
- “对整棵结构做某种变换”常能归约成“对根做一步局部操作 + 对子结构递归同样的操作”;
- 交换两个引用用元组赋值最干净,省掉临时变量;
- 判断递归顺序是否要紧,只需看这一步会不会破坏后续递归依赖的结构。
能就地改结构的树问题,通常比“重建一棵新树”写得更短。