镜像翻转是递归交换每个非空节点的left和right指针以原地翻转二叉树;核心为std::swap(node->left, node->right),需自顶向下递归或用栈迭代,终止条件必须为if(!root),时间复杂度O(n),空间复杂度O(h)。

什么是镜像翻转?就是递归交换每个节点的 left 和 right 指针
镜像翻转不是创建新树,而是原地修改指针指向。核心动作只有一个:对每个非空节点,把它的 left 和 right 成员变量互换。必须自顶向下递归,且交换发生在递归回退前(即先换再递归子树,或先递归再换——二者等价,但顺序影响可读性)。
常见错误是只交换了当前层,忘了递归处理子树;或者递归了但没交换,导致“翻了又翻”变回原样。
- 必须在递归调用前或后执行
swap(node->left, node->right),不能省略 - 空节点(
nullptr)直接返回,不操作 - 交换本身是 O(1),整棵树时间复杂度 O(n),空间复杂度 O(h),h 为树高(递归栈深度)
std::swap 还是手动赋值?推荐用 std::swap
手动写临时变量交换容易出错,比如写成 tmp = left; left = right; right = tmp; 看似正确,但若指针类型重载了移动语义,std::swap 更安全通用。C++11 起,std::swap 对原始指针有特化,效率一样,代码更清晰。
示例片段:
立即学习“C++免费学习笔记(深入)”;
void mirror(TreeNode* root) {
if (!root) return;
std::swap(root->left, root->right);
mirror(root->left);
mirror(root->right);
}- 不要用
root->left = root->right; root->right = root->left;—— 这会丢失左子树 - 如果用 C++17 及以上,也可用结构化绑定 +
std::tie,但没必要,std::swap最直白 - 注意:
TreeNode类型需是你定义的,字段名必须是left和right,否则编译失败
递归终止条件写错会导致栈溢出或段错误
最典型的错误是把 if (!root) 写成 if (root == nullptr) —— 语义相同,但问题不在这里;真正坑人的是漏判或误判空指针。例如写成 if (root->left == nullptr && root->right == nullptr),这只会对叶子节点操作,完全跳过内部节点。
- 终止条件只能是
if (!root)或if (root == nullptr),判断当前节点是否为空 - 不能依赖子节点状态来决定是否继续递归,否则中间节点被跳过
- 如果输入可能是野指针(未初始化的
root),建议加assert(root != nullptr || "root is null")调试时捕获
非递归实现用栈模拟,但要注意入栈顺序
想避免递归栈溢出(比如极深的偏斜树),可用显式栈。关键点在于:每次弹出一个节点,交换其左右指针,然后把非空子节点压栈。但压栈顺序必须是先 left 后 right,还是先 right 后 left?答案是:只要后续处理一致,不影响结果;但为和递归逻辑对齐,推荐先压 right、再压 left,这样出栈顺序和递归访问顺序一致。
示例:
void mirrorIterative(TreeNode* root) {
if (!root) return;
std::stack<TreeNode*> stk;
stk.push(root);
while (!stk.empty()) {
TreeNode* node = stk.top(); stk.pop();
std::swap(node->left, node->right);
if (node->left) stk.push(node->left);
if (node->right) stk.push(node->right);
}
}- 这里压栈顺序是 left → right,和递归版的调用顺序相反,但最终效果一样
- 若要严格复现递归行为(比如调试对比),压栈顺序应为 right → left
- 迭代法空间复杂度仍是 O(h),最坏情况栈内存最多存 h 个节点
实际写的时候,递归版本更直观;但线上服务处理未知深度的树时,得考虑栈大小限制。镜像翻转本身逻辑极简,难点全在指针操作的时机和边界判断上。


















