Morris前序遍历必须严格判断cur->left==nullptr以确保安全边界,否则会导致死循环或遗漏节点;访问需在cur非空时立即执行,且临时右链必须成对建立与清除。

Morris前序遍历能真正做到 O(1) 空间复杂度,但它的「临时修改树结构」行为极易引发段错误或逻辑错乱——尤其在多线程、调试中反复运行、或节点被复用的场景下。
为什么cur->left == nullptr时直接访问并跳右,不能省略?
这是整个流程的安全边界。一旦跳过该判断,cur 为 nullptr 时会进入死循环;更隐蔽的问题是:若某节点左子树为空但右子树也为空,漏掉这个分支会导致直接退出,遗漏当前节点。必须严格按「空左→访问→跳右」执行。
-
cur为nullptr时,while 循环自然终止,不需额外判空 - 访问操作
result.push_back(cur->val)必须在确认cur != nullptr后立即进行,不能延迟到后续分支 - 跳转语句
cur = cur->right是唯一出口,不可被任何 else 或嵌套条件拦截
node->right == cur 判断失败的常见原因
这不是简单的指针相等比较,而是依赖于 Morris 过程中是否已建立过临时链接。如果此前未走完完整流程(比如中途 return、异常退出、或测试用例复用同一棵树多次),node->right 可能仍为 nullptr 或指向旧地址,导致误判为「第一次访问」而重复建链,最终形成环或崩溃。
- 每次调用前必须确保树是「干净」的——即所有
right指针都处于原始状态 - 不要在同一个
TreeNode*实例上连续调用两次solution2(),除非显式恢复(如重置所有right) - 调试时打印
node->right值可快速定位是否已有链接残留:if (node->right == nullptr) { /* 第一次 */ } else if (node->right == cur) { /* 第二次 */ }
前序和中序 Morris 的关键差异在哪?
仅在「访问时机」:前序要求「一见到节点就访问」,中序则必须「从左子树返回后才访问」。这意味着前序在找到前驱并建链后立刻 push_back(cur->val),而中序要把这行挪到「发现 node->right == cur 之后、恢复指针之前」。
- 前序:建链分支里有
result.push_back(cur->val);恢复分支里没有 - 中序:建链分支里无访问;恢复分支里有
result.push_back(cur->val) - 二者共用同一套找前驱逻辑(
while(node->right && node->right != cur)),不可混用访问位置
真正难的不是写对一次,而是让 solution2() 在不同输入、多次调用、甚至与其他遍历混用时都不破坏树结构——临时链接必须成对出现且成对清除,少一次 node->right = nullptr,下次遍历就可能无限循环。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!











