非递归实现需重设计状态管理逻辑:DFS须push前标记visited,快排只压较小区间以控栈深,中序遍历需严格遵循“左压→弹访→右转”流程,手写栈时须注意初始化与边界检查。

递归深度超过系统栈限制(Linux 默认约 8MB,Windows 约 1MB)时,Segmentation fault 或 stack overflow 不是“运气不好”,而是必然结果。改用显式栈(std::stack 或自定义堆栈)是唯一可靠解法,核心不是“换写法”,而是重设计状态管理逻辑。
非递归 DFS:必须在 push 前标记 visited
这是最容易踩的坑——很多人照着递归逻辑“翻译”,却忽略访问顺序和标记时机的差异。
- 错误做法:
pop()后才设visited[u] = true→ 同一节点可能被多次压入栈(尤其图中多路径可达时) - 正确做法:在
push()前就标记visited[u] = true,确保每个节点最多入栈一次 - 典型场景:链状图(
1→2→3→…→10⁵)或迷宫长路径,递归 DFS 深度轻易破万,非递归版无压力
快排非递归实现:只压较小子区间,防最坏 O(n) 栈深
单纯把两个递归调用改成 push() 并不能解决最坏情况——已排序数组仍会压入 O(n) 层区间。关键优化是“尾递归消除 + 子区间大小裁剪”。
- 每次只将 较小 的子区间压栈,较大区间用循环处理(模拟尾递归)
- 这样栈深从 O(n) 降至 O(log n),即使极端输入也不爆栈
-
partition()函数本身无需改动,但调用逻辑要重排:先处理[low, pi-1]或[pi+1, high]中长度更短的那个
二叉树遍历非递归:中序需分三步压栈/弹栈/转向
中序遍历的非递归逻辑不是简单模拟递归调用栈,而是利用“左到底→弹→访→右”的状态机模型。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
立即学习“C++免费学习笔记(深入)”;
- 常见错误:把
visit()放在压栈循环里 → 实际变成前序遍历 - 正确结构:
while (curr || !stk.empty()) { while (curr) { stk.push(curr); curr = curr->left; } // 一路向左压 curr = stk.top(); stk.pop(); visit(curr); // 弹栈即访问 curr = curr->right; // 切入右子树 } - 漏掉
curr = curr->right会导致死循环;只判!stk.empty()忽略curr会漏掉最右分支
手写 stack 替代 std::stack:当需要控制内存或避免异常时
std::stack 默认基于 std::deque,扩容安全但有额外开销;竞赛或嵌入式场景下,你可能需要确定性行为和零异常保证。
- 用
std::vector作底层容器:std::stack<int std::vector>></int>,扩容策略更可预测 - 完全手动管理:
int* data+int top+int capacity,push()前检查并realloc(),适合对栈深有硬上限的场景(如固定大小迷宫) - 注意:手写栈必须显式初始化
top = 0,否则未定义行为;pop()前必须assert(!empty()),否则越界读
真正难的不是“怎么写 stack”,而是判断哪些状态该入栈、何时标记、哪部分该用循环代替——这些决策直接决定栈深和正确性。一个没想清的 visited 位置,或一次没裁剪的子区间,就可能让非递归代码退化回递归的脆弱性。

















