Morris后序遍历必须翻转右子路径,因为后序要求左右子树访问完再访根,而Morris单向推进无回溯,需通过翻转临时右链获取逆序访问路径,再遍历还原以保证正确顺序和指针安全。

为什么 Morris 后序遍历必须翻转右子路径
Morris 后序遍历无法像前序/中序那样直接在“访问时机”上做文章,因为后序要求左右子树都处理完再访问根——而 Morris 的线索化过程是单向推进的,没有回溯指针。核心解法是:在每个 current 节点找到其左子树的最右节点(即前驱)时,临时建立线索并**反向遍历从该前驱到 current 的右链**,这条链恰好对应后序中“当前子树的逆序访问路径”。不翻转就拿不到正确的访问顺序。
常见错误现象:vector 输出顺序乱、漏节点、重复访问根节点——基本都是跳过了翻转步骤或翻转后没还原右指针。
- 翻转操作只作用于临时构建的右链(即
predecessor->right到current这一段),不是整棵树 - 每次翻转后必须再走一遍该链完成访问,并在退出前把右指针全部还原为
nullptr - 翻转逻辑要能处理单节点链(避免空指针解引用)
如何安全地翻转并还原右子路径
翻转本身是链表反转的经典操作,但关键在于边界控制和还原时机。Morris 后序中,待翻转链起始于 predecessor,终止于 current,且整条链由 right 指针串起(注意:不是 left!)。
实操建议:
- 用三个指针
prev、curr、next做原地翻转,初始prev = nullptr,curr = predecessor - 循环条件是
curr != current,不是curr != nullptr——否则会越界到无关节点 - 翻转完成后,从新头节点(原
predecessor的末端,即prev)开始遍历,把每个节点值加入结果;遍历完立即把该路径上所有right指针设回nullptr - 务必在还原前保存
prev的原始right(即current),否则丢失线索连接点
示例片段(翻转+访问+还原):
TreeNode* tail = reverseRightPath(predecessor, current);
for (TreeNode* p = tail; p != nullptr; ) {
result.push_back(p->val);
TreeNode* next = p->right;
p->right = nullptr; // 还原
p = next;
}
完整 Morris 后序的主循环怎么写才不出错
主循环结构比中序更紧凑,但条件分支更敏感。核心是区分两种状态:是否已建立线索,以及是否刚完成一次翻转访问。
- 若
current->left == nullptr,说明无左子树,直接访问 current 并进入current = current->right - 否则找
predecessor(左子树最右节点),分两支:- 若
predecessor->right == nullptr:建线索predecessor->right = current,然后current = current->left - 若
predecessor->right == current:说明左子树已处理完,此时触发翻转路径、访问、还原;最后predecessor->right = nullptr断线索,current = current->right
- 若
- 特别注意:只有第二种情况才执行翻转逻辑,且翻转前必须确保
predecessor != nullptr(空左子树不会进这个分支,但代码里仍建议加判空) - 不要在翻转后遗漏
current = current->right,否则陷入死循环
容易被忽略的边界与性能坑
实际调试中最常卡住的不是算法逻辑,而是这些细节:
-
predecessor查找循环中,判断条件必须是predecessor->right != nullptr && predecessor->right != current,少一个条件就会无限循环或跳过线索检测 - 树为空(
root == nullptr)时,直接返回空vector,别让主循环跑起来 - 单节点树:不进左子树分支,走
current->left == nullptr分支,但之后current = current->right会变成nullptr,循环自然退出——没问题,但新手常在这里多加一层判断反而出错 - 时间复杂度仍是 O(n),但常数因子比递归大;空间确实是 O(1) —— 不含输出空间,也不用栈/队列
真正复杂的是翻转链的指针操作密度:每条右链平均被翻转、遍历、还原三次(建立线索时一次,访问时一次,还原时一次),写错一个指针方向就全乱。动手前先在纸上画清三节点链的翻转前后指向关系,比看十遍代码管用。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











