void PreOrder(BiTree T) {
if (T != NULL) {
visit(T); // 访问根结点
PreOrder(T->lchild); // 遍历左子树
PreOrder(T->rchild); // 遍历右子树
}
}
void InOrder(BiTree T) {
if (T != NULL) {
InOrder(T->lchild); // 遍历左子树
visit(T); // 访问根结点
InOrder(T->rchild); // 遍历右子树
}
}
void PostOrder(BiTree T) {
if (T != NULL) {
PostOrder(T->lchild); // 遍历左子树
PostOrder(T->rchild); // 遍历右子树
visit(T); // 访问根结点
}
}
void LevelOrder(BiTree T) {
Queue Q;
InitQueue(&Q);
EnQueue(&Q, T);
while (!QueueEmpty(Q)) {
BiTNode *p;
DeQueue(&Q, &p);
visit(p);
if (p->lchild) EnQueue(&Q, p->lchild);
if (p->rchild) EnQueue(&Q, p->rchild);
}
}
A
/ \
B C
/ \ \
D E F
/
G
| 遍历方式 | 序列 |
|---|---|
| 先序(根左右) | A B D E G C F |
| 中序(左根右) | D B G E A C F |
| 后序(左右根) | D G E B F C A |
| 层序(队列) | A B C D E F G |
D B G E,右边是右子树 C F。做题时用这两条快速自查。void InOrderNonRec(BiTree T) {
SqStack S;
InitStack(&S);
BiTNode *p = T;
while (p || !StackEmpty(S)) {
if (p) {
Push(&S, p); // 一路向左,边走边入栈
p = p->lchild;
} else {
Pop(&S, &p); // 出栈
visit(p); // 访问
p = p->rchild; // 转向右子树
}
}
}
void PreOrderNonRec(BiTree T) {
SqStack S;
InitStack(&S);
BiTNode *p = T;
while (p || !StackEmpty(S)) {
if (p) {
visit(p); // 与中序唯一的区别:入栈前先访问
Push(&S, p);
p = p->lchild;
} else {
Pop(&S, &p);
p = p->rchild;
}
}
}
需要用 r 记录上一个访问过的结点,以区分「右子树还没走」和「右子树刚走完」:
void PostOrderNonRec(BiTree T) {
SqStack S;
InitStack(&S);
BiTNode *p = T, *r = NULL; // r 记录上次访问的结点
while (p || !StackEmpty(S)) {
if (p) {
Push(&S, p);
p = p->lchild;
} else {
GetTop(S, &p); // 只看栈顶,不弹出
if (p->rchild && p->rchild != r) // 右子树未访问
p = p->rchild;
else {
Pop(&S, &p);
visit(p);
r = p; // 记录刚访问的结点
p = NULL; // 防止重复向左
}
}
}
}
GetTop 而非 Pop 探查;② 用 r 判断右子树是否已访问;③ 访问后必须把 p 置 NULL,否则会重新向左下沉造成死循环。| 已知序列组合 | 能否唯一确定 | 原因 |
|---|---|---|
| 先序 + 中序 | ✅ | 先序给根,中序给左右分界 |
| 后序 + 中序 | ✅ | 后序末位给根,中序给分界 |
| 层序 + 中序 | ✅ | 层序首位给(子树)根 |
| 先序 + 后序 | ❌ | 无中序,无法区分「只有左孩子」与「只有右孩子」 |
先序:A B D E C F 中序:D B E A F C
D B E 是左子树(3 个结点),右边 F C 是右子树(2 个结点);B D E、中序 D B E → B 是根,D 左、E 右;C F、中序 F C → C 是根,F 是左孩子。 A
/ \
B C
/ \ /
D E F
后序:D E B F C A 中序:D B E A F C
D B E、右边 F C;D E B → B 是根(后序末位),D 左、E 右;F C → C 是根,F 是左孩子。得到与例 1 完全相同的树 ✓
A
/ \
B C
/ \
D E
| 步 | 动作 | 栈 | 输出 |
|---|---|---|---|
| 1 | p=A,入栈,p=B | [A] | — |
| 2 | p=B,入栈,p=D | [A,B] | — |
| 3 | p=D,入栈,p=NULL | [A,B,D] | — |
| 4 | 出栈 D,访问,p=D.rchild=NULL | [A,B] | D |
| 5 | 出栈 B,访问,p=B.rchild=E | [A] | D B |
| 6 | p=E,入栈,p=NULL | [A,E] | D B |
| 7 | 出栈 E,访问,p=NULL | [A] | D B E |
| 8 | 出栈 A,访问,p=A.rchild=C | [] | D B E A |
| 9 | p=C,入栈,p=NULL | [C] | D B E A |
| 10 | 出栈 C,访问,p=NULL | [] | D B E A C |
| 11 | 栈空且 p=NULL → 结束 | [] | D B E A C |
ABC、后序 CBA 能确定二叉树吗?不能 —— 先序 + 后序不能唯一确定。r 记录上一个访问的结点。| 组合 | 能否唯一确定 |
|---|---|
| 先序 + 中序 | ✅ |
| 后序 + 中序 | ✅ |
| 层序 + 中序 | ✅ |
| 先序 + 后序 | ❌ |
时间复杂度:四种遍历均为 $O(n)$。
空间复杂度:递归 / 栈为 $O(h)$($h$ 为树高,最坏 $O(n)$);层序队列最坏 $O(n)$。
↑ 以上为站内 HTML 相对链接(纯网页可浏览);本页右上「在 Obsidian 中打开」跳回源笔记。