镜像翻转是原地交换二叉树每个节点的left和right指针,递归实现需先递归左右子树再交换,避免重复或漏翻转;常见错误包括空指针访问、交换顺序错误及栈溢出。

什么是镜像翻转?关键在于原地交换
镜像翻转不是新建一棵树,而是把原二叉树每个节点的 left 和 right 指针直接互换。递归是最自然的实现方式,但必须注意:先递归子树再交换,还是先交换再递归?答案是——先递归再交换,否则会重复翻转或漏翻转。
常见错误现象:nullptr 访问崩溃、翻转后部分子树没变、递归栈溢出(深度过大时)。这通常是因为没判空,或用了非尾递归且树退化成链表。
- 务必在函数开头检查
root == nullptr,直接返回 - 交换操作必须放在左右子树递归调用之后(后序遍历逻辑)
- 如果担心栈溢出,可用显式栈模拟迭代版,但日常使用递归足够
递归实现:三行核心代码搞定
标准写法就是“递归左、递归右、交换当前节点”。C++ 中交换指针用 std::swap 最安全,避免手写临时变量出错。
void mirrorTree(TreeNode* root) {
if (!root) return;
mirrorTree(root->left);
mirrorTree(root->right);
std::swap(root->left, root->right);
}为什么不用 root->left = root->right; root->right = root->left;?因为第二行赋值时 root->left 已被覆盖,结果两个子指针都指向同一侧。这是初学者高频踩坑点。
立即学习“C++免费学习笔记(深入)”;
-
std::swap是原子操作,无需额外判空 - 函数签名用
TreeNode*而非TreeNode&,因为树节点指针本身是值传递,不影响调用方指针变量 - 该函数不返回新根,也不修改
root地址,只改其成员指针
迭代实现:用栈模拟后序遍历
想避开递归?可以用栈保存待处理节点,但要注意:后序遍历的迭代写法比前序复杂。更稳妥的做法是用队列做层序遍历,对每层每个节点都交换左右子指针。
层序版本更直观,也更容易调试:
void mirrorTree(TreeNode* root) {
if (!root) return;
std::queue<TreeNode*> q;
q.push(root);
while (!q.empty()) {
TreeNode* node = q.front(); q.pop();
std::swap(node->left, node->right);
if (node->left) q.push(node->left);
if (node->right) q.push(node->right);
}
}- 层序版时间复杂度仍是 O(n),空间复杂度 O(w),w 为最大宽度(最坏 O(n))
- 相比递归,它不会因深度导致栈溢出,适合已知可能退化为链表的场景
- 注意:不能只 push 非空子节点,否则
nullptr子节点会被跳过,但交换本身不需要处理空指针
验证镜像是否成功:别只看打印
翻转后怎么确认是对的?光靠中序/前序遍历输出看不出问题。最可靠的方式是写一个对比函数:把原树和翻转后树按镜像规则比对 —— 即 tree1->left 应等于 tree2->right(递归比较)。
但更常用的是构造小样例手动验证,比如输入 [1,2,3](层序),期望输出仍是 [1,3,2]。注意 LeetCode 的 TreeNode 构造函数常隐含默认值,容易误判。
- 测试时务必包含空树、单节点、只有左子树/右子树的 case
- 交换后原
root地址不变,所以若外部保存了某个子节点指针,它仍有效;但该子节点的左右关系已变 - 如果树里有 parent 指针,镜像翻转后需同步更新,但标准题设通常不涉及
实际写的时候,递归版三行足够,但得记牢交换必须在递归之后;迭代版稍长但可控。最容易被忽略的是:交换语句写错顺序,或者忘了判空导致崩溃。


















