二叉树镜像反转本质是递归交换每个非空节点的左右子树指针,需递归至叶子节点,推荐先交换再递归,C++中应使用std::swap并包含<utility>头文件。

镜像反转的本质是递归交换左右子树
二叉树镜像反转不是翻转节点值,而是让每个节点的 left 和 right 指针互换。核心动作就一个:对每个非空节点,执行 swap(node->left, node->right),然后递归处理左右子树。
最容易错的是只交换一次就停——必须递归到叶子节点,否则深层子树没被翻转。比如根节点交换后,若不递归进原左子树(现右子树),它内部结构仍保持原样。
- 递归终止条件必须是
node == nullptr,不能写成!node后漏掉空指针检查(虽等价但可读性差) - 交换操作必须在递归调用前还是后?其实都行,但推荐先交换再递归:保证当前层已翻转,子树递归时面对的是“新布局”
- 不要试图用栈模拟递归时忘记 push 交换后的子节点——顺序搞反会导致部分子树被跳过
C++中用 std::swap 还是手动赋值
两者都能工作,但 std::swap 更安全、更符合 C++ 惯例。手动赋值如 auto tmp = node->left; node->left = node->right; node->right = tmp; 容易因变量名 typo 或漏写某一行出错;而 std::swap 是原子语义,且对指针类型零开销。
注意:如果节点指针类型是智能指针(如 std::unique_ptr<treenode></treenode>),std::swap 依然适用,且会正确转移所有权;手动赋值则可能触发意外拷贝或编译失败。
立即学习“C++免费学习笔记(深入)”;
- 头文件别忘加
#include <utility>(C++11 起std::swap在此) - 若使用自定义类且未提供
swap成员函数,std::swap会退回到拷贝赋值,此时性能较差——但 TreeNode 指针场景无此问题 - 禁止用宏
#define swap(a,b) ...替代,容易和标准库冲突
迭代实现时栈里该存什么
迭代法本质是手动模拟递归栈,关键在于:每次从栈中取出一个节点后,必须立刻交换它的左右指针,再把非空的子节点压栈。顺序无关紧要(先左后右 or 先右后左),但必须确保两个子节点都被压入——否则遗漏分支。
常见错误是只压入一个子节点,或压入前没判断空指针导致崩溃;另一个坑是交换后又把原 left 和 right 压栈(没更新引用),结果翻转失效。
- 推荐用
std::stack<treenode></treenode>,初始化时压入根节点 - 循环体中:弹出
node→std::swap(node->left, node->right)→ 若node->left非空则压入 → 若node->right非空则压入 - 不要用
queue做层序迭代翻转——虽然也能实现,但逻辑绕,且容易混淆“访问顺序”和“交换时机”
反转后如何验证结果是否正确
不能只靠肉眼比对打印结果。最直接的方法是:对原树做先序遍历,对镜像树做“根-右-左”遍历(即镜像先序),两者序列应完全一致。或者更稳妥地,写一个递归比较函数:isMirror(TreeNode* a, TreeNode* b),检查 a->left 是否等于 b->right 的结构。
调试时建议先用最小用例:单节点、两层满树(3节点)、带空子节点的树(如根→左→左)。特别注意空指针解引用——比如某节点只有右子树,反转后左指针为 nullptr,若验证代码没判空就会崩。
- 避免在验证函数里写
a->val == b->val && isMirror(a->left, b->right) && isMirror(a->right, b->left)而不先判空,这是段错误高发区 - 单元测试至少覆盖:空树、单节点、左斜树、右斜树、完全二叉树
- 如果树节点含额外字段(如
height或parent),镜像后这些字段不会自动更新——需单独处理,这点常被忽略


















