Morris前序遍历空间复杂度为O(1),因其不使用递归或显式栈,仅通过临时修改空右指针建立线索,并用curr、prev两个指针驱动;访问节点时机是进入curr时立即加入结果,无论是否有左子树。

为什么 Morris 前序遍历能 O(1) 额外空间?
因为不递归、不显式用栈,只靠临时修改树中空右指针(nullptr)建立线索,遍历完再复原。真正额外变量只有 curr、prev 两个指针 —— 空间复杂度确为 O(1),和树高、节点数都无关。
但注意:这个 O(1) 是指「额外辅助空间」,不包括输出数组(存结果);若题目要求返回 vector<int>,那输出空间仍是 O(n),这是合理且不可省的。
前序遍历中,什么时候该访问节点?
Morris 前序的关键判断点是:当 curr->left == nullptr,或 curr 的左子树最右节点已指向 curr(即线索已存在)时,才真正访问 curr 并推进到右子树。
但注意:前序要“先访问再走”,所以只要进入 curr,就立即把 curr->val 加入结果——哪怕它还有左子树。这和中序不同(中序要等左子树全处理完才访问)。
立即学习“C++免费学习笔记(深入)”;
常见错误是把访问时机错当成中序逻辑,导致重复访问或漏访问。
- 若
curr->left == nullptr:直接访问curr,然后curr = curr->right - 若
curr->left != nullptr:先找左子树最右节点prev;若prev->right == nullptr,说明未建线索 → 设prev->right = curr,然后curr = curr->left(继续向左探) - 若
prev->right == curr,说明线索已存在、左子树已处理过 → 拆线(prev->right = nullptr),然后curr = curr->right
findRightmost 辅助函数怎么写才不出错?
这个函数用于在 curr->left 子树里找最右节点(即中序意义下 curr 的前驱)。必须严格满足:只往右走,且跳过已建好的线索(即不走进 curr 自身)。
典型错误是条件写成 node->right != nullptr 就继续,结果误入刚建好的线索,造成死循环。
正确写法:
TreeNode* findRightmost(TreeNode* node) {
while (node->right && node->right != curr) {
node = node->right;
}
return node;
}注意:参数 node 是 curr->left,且函数内必须捕获外层的 curr(通常放循环体内直接写逻辑更安全,避免闭包问题)。
为什么恢复树结构很重要?
Morris 遍历会临时修改 right 指针,如果中途异常退出(如被调试中断、或上层代码假设树只读)、或后续还要对同一棵树做其他遍历(比如再跑一次中序),没复原就会导致指针错乱、无限循环甚至段错误。
所有线索都必须在「确认已用过」后立刻还原:prev->right = nullptr 这一行不能省,也不能只在部分分支执行。
另一个易忽略点:若输入为空树(root == nullptr),函数应直接返回空结果,不进入任何循环 —— 否则 curr 初始化失败,引发未定义行为。


















