板子:非递归遍历(中序、前序、后序)
用栈模拟递归:一路向左,边走边入栈;走到空,就弹出一个结点,转向它的右子树。中序和前序只差
visit的位置;后序多一个指针r,记住「上一个访问的结点」。
代码
void InOrder2(BiTree T) { // 中序非递归 ★★★
BiTNode *st[MaxSize]; // 栈里存结点指针
int top = -1;
BiTNode *p = T;
while (p != NULL || top != -1) { // 还有没走到的结点,或者栈里还有结点
if (p != NULL) { // 一路向左,边走边入栈
st[++top] = p;
p = p->lchild;
} else { // 左边走到头了
p = st[top--]; // 弹出一个:它的左子树已经处理完
visit(p); // 中序:出栈时访问
p = p->rchild; // 转向右子树
}
}
}
void PreOrder2(BiTree T) { // 前序非递归:和中序只差 visit 的位置
BiTNode *st[MaxSize];
int top = -1;
BiTNode *p = T;
while (p != NULL || top != -1) {
if (p != NULL) {
visit(p); // 前序:入栈时访问
st[++top] = p;
p = p->lchild;
} else {
p = st[top--];
p = p->rchild;
}
}
}
void PostOrder2(BiTree T) { // 后序非递归
BiTNode *st[MaxSize];
int top = -1;
BiTNode *p = T, *r = NULL; // r:上一个访问的结点
while (p != NULL || top != -1) {
if (p != NULL) { // 一路向左
st[++top] = p;
p = p->lchild;
} else {
p = st[top]; // 只读栈顶、先不出栈:它的右子树可能还没走
if (p->rchild != NULL && p->rchild != r)
p = p->rchild; // 右子树存在且还没访问过:先去右子树
else { // 右子树为空,或者刚从右子树回来:可以访问根了
top--;
visit(p);
r = p; // 记下刚访问的结点
p = NULL; // 必须置空:下一轮直接看新的栈顶,不能再往左走
}
}
}
}复杂度:时间
关键边界(后序)
| 代码 | 为什么 |
|---|---|
p = st[top] 只读不出栈 | 从左子树回来时右子树还没走,根要继续留在栈里 |
p->rchild != r | 用来判断「刚从右子树回来」:右孩子正好是上一个访问的结点 |
访问后 p = NULL | 访问完根,下一步应该回到它的双亲。不置空的话,下一轮会把 p 再次入栈,陷入死循环 |
后序非递归的一个性质
访问某个结点时,栈里从底到顶正好是它的所有祖先(从根往下排)。找「值为
易错点
- 循环条件是
p != NULL || top != -1,两个条件缺一不可。刚开始时栈是空的但p不空;中途p为空但栈不空。 - 栈里存的是结点指针
BiTNode *,不是int。 - 前序非递归有另一种写法:根入栈,每次弹出一个结点后先压右孩子、再压左孩子。两种都对,这里的写法和中序统一,更好记。
链接
- 📕 返回:数据结构代码板子
- ⬅️ 上一篇:层序遍历与按层处理
- ➡️ 下一篇:靠递归返回值的题
- 🔗 递归版本:前序、中序、后序递归遍历
- 🔗 5.3.1 二叉树的遍历