一、先序遍历
1.1 步骤
- 访问根节点
- 先序遍历左子树
- 先序遍历右子树
1.2 递归代码
void PreOrder(BiTree T) {
if(T != NULL) {
visit(T);
PreOrder(T->lchild);
PreOrder(T->rchild);
}
}
1.3 非递归代码
void PreOrder(BiTree T) {
BiTree p = T;
InitStack(S);
while(p || !IsEmpty(S)) {
if(p) {
visit(p);
Push(s, p);
p = p->lchild;
} else {
Pop(s, p);
p = p->rchild;
}
}
}
二、中序遍历
2.1 步骤
- 中序遍历左子树
- 访问根节点
- 中序遍历右子树
2.2 递归代码
void InOrder(BiTree T) {
if(T != NULL) {
InOrder(T->lchild);
visit(T);
InOrder(T->rchild);
}
}
2.3 非递归代码
- 沿着根的左孩子,依次入栈,直到左孩子为空
- 栈顶元素出栈并访问,若其右孩子为空,继续执行第二步;若其右孩子不为空,将右子树转执行第一步。
void PreOrder(BiTree T) {
BiTree p = T;
InitStack(S);
while(p || !IsEmpty(S)) {
if(p) {
Push(s, p);
p = p->lchild;
} else {
Pop(s, p);
visit(p);
p = p->rchild;
}
}
}
三、后序遍历
3.1 步骤
- 后序遍历左子树
- 后序遍历右子树
- 访问根节点
3.2 递归代码
void PostOrder(BiTree T) {
if(T != NULL) {
PostOrder(T->lchild);
PostOrder(T->rchild);
visit(T);
}
}
3.3 非递归代码
- 从根节点开始,将其入栈,然后沿其左子树一直往下探索,直到搜索到没有左孩子的结点
- 此时不能出栈并访问,因为如果其有右子树,还需按照相同的规则对其右子树进行处理
- 当操作进行不下去,若栈顶元素想要出栈被访问,要么右子树为空,要么右子树刚被访问完(此时左子树早已访问完)
void PostOrder(BiTree T) {
InitStack(S);
BiTNode *p = T;
BiTNode *r = NULL;
while(p || !IsEmpty(S)) {
if(p) {
Push(S, p);
p = p->lchild;
} else {
GetTop(S, p);
if(p->rchild && p->lchild != r) {
p = p->rchild;
} else {
Pop(S, p);
visit(p);
r = p;
p = NULL;
}
}
}
}
- 每次出栈访问完一个结点就相当于遍历完以该节点为根的子树,需要将
p
p
p置为
N
U
L
L
NULL
NULL。
3.4 复杂度
- 三种遍历算法的时间复杂度均为
O
(
n
)
O(n)
O(n)
- 空间复杂度为
O
(
n
)
O(n)
O(n)
四、层次遍历
4.1 步骤
- 将二叉树根节点入队,然后出队,访问出队结点;
- 若有左子树,则将左子树根节点入队;
- 若有右子树,则将右子树根节点入队;
- 完成入队后出队,访问出队结点;
- 如此反复,直到队列为空。
4.2 代码
void LevelOrder(BiTree T) {
InitQueue(Q);
BiTree p;
EnQueue(Q, T);
while(!IsEmpty(Q)) {
DeQueue(Q, p);
visit(p);
if(p->lchild != NULL) EnQueue(Q, p->lchild);
if(p->rchild != NULL) EnQueue(Q, p->rchild);
}
}