原地翻转指仅交换节点指针而不新建treenode对象,避免破坏外部指针引用;递归需先递归子树再交换左右指针,迭代用栈显式管理待处理节点,且必须判空并完整压入非空子节点。

什么是原地翻转,为什么不能新建节点
原地翻转意味着只交换指针,不分配新 TreeNode 对象。新建节点看似简单,但会破坏原树结构引用,尤其当外部持有原始指针(如父节点、容器中存储的指针)时,翻转后旧指针仍指向原子树,导致逻辑错乱或内存泄漏。
关键判断:只要函数签名是 void mirrorTree(TreeNode* root) 且不 new 节点,就是原地操作;一旦出现 new TreeNode(...),就不是原地。
递归实现:必须先翻转子树再交换左右指针
常见错误是先交换 left 和 right,再递归——这会导致某一边被翻转两次。正确顺序是:递归处理左右子树 → 再交换当前节点的 left 和 right 指针。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
- 递归基:空节点直接返回,不操作
- 递归调用必须覆盖左右子树,否则漏翻转
- 交换语句必须写在递归调用之后,否则子树未翻转就换指针,逻辑错位
void mirrorTree(TreeNode* root) {
if (!root) return;
mirrorTree(root->left);
mirrorTree(root->right);
std::swap(root->left, root->right); // 或手动 tmp = left; left = right; right = tmp;
}
迭代实现:用栈模拟递归,避免隐式调用栈开销
迭代法本质是把“待翻转节点”压入栈,每次弹出一个,交换其左右指针,再把非空子节点压入——注意:左右子节点要**都压入**,顺序不影响结果,但不能只压一边。
- 若用
queue替代stack,效果相同(BFS序),但习惯上用栈更贴近递归逻辑 - 必须检查
node->left和node->right是否为空再压入,否则空指针入栈导致崩溃 - 交换操作和递归版完全一致,只是控制流由栈显式管理
void mirrorTree(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);
}
}</treenode>
测试时容易忽略的边界:空树、单节点、只有左/右子树
很多实现能过样例但挂掉边界用例,比如:nullptr 输入未判空、单节点未触发交换、只有左子树时右子树为 nullptr 却未正确交换——这些都会导致段错误或逻辑错误。
- 务必用
if (!root)开头,哪怕看起来“不可能” - 单节点树:
left和right均为nullptr,swap安全,无需额外 guard - 只有左子树:翻转后应变成只有右子树,检查输出结构是否符合预期,而非只看打印值
真正容易被忽略的是:翻转后原 root->left 变成 root->right,所有基于原指针的后续访问(比如遍历前序)必须用新结构,否则行为未定义。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










