遍历的概念
顺着某一条搜索路径巡访二叉树中的结点,使得每个结点均被访问一次,而且仅被访问一次。
“访问”的含义可以很广,如:输出结点的信息等。
“遍历”是任何类型均有的操作,对线性结构而言,只有一条搜索路径(因为每个结点均只有一个后继),故不需要另加讨论。而二叉树是非线性结构,每个结点有两个后继,则存在如何遍历即按什么样的搜索路径进行遍历的问题。
搜索路径
对于“二叉树”而言,可以有3条搜索路径:
- 先上后下的按层次遍历;
- 先左(子树)后右(子树)的遍历;
- 先右(子树)后左(子树)的遍历。
遍历算法(非递归)
先序遍历
若二叉树为空树,则空操作;否则,
- 访问根结点;
- 先序遍历左子树;
- 先序遍历右子树。
中序遍历
若二叉树为空树,则空操作;否则,
- 中序遍历左子树;
- 访问根节点;
- 中序遍历右子树。
后序遍历
若二叉树为空树,则空操作;否则,
- 后序遍历左子树;
- 后序遍历右子树。
- 访问根节点;
先序遍历代码描述
void preOrder(BiTree T, void(*visit)(int &e){
//先序遍历二叉树
if(T){
visit(T->data); //访问节点
preOrder(T->lchild, visit); //遍历左子树
preOrder(T->richild, visit); //遍历右子树
}
}
遍历算法(递归)
中序遍历
由中序遍历过程可知,采用一个栈保存需要返回的结点指针,先扫描(并非访问)根结点的所有左结点并将它们一一进栈。
然后出栈一个结点,显然该结点没有左孩子结点或者左孩子结点已访问过,则访问它。
然后扫描该结点的右孩子结点,将其进栈,再扫描该右孩子结点的所有左结点并一一进栈,如此这样,直到栈空为止。
void inOrder(BtTree root){
initStack(&S); //初始化栈
p = root;
while(p != NULL || !StackEmprt(S)){
if(p != NULL){
//根指针进栈,遍历左子树
Push(&S, p);
p = p->lchild;
} else {
//根指针出栈,访问根节点,遍历右子树
Pop(%S, p);
Visit(p->data); //访问节点
p = p->rchild;
}
}
}
先序遍历
由先序遍历过程可知,先访问根结点,再访问左子树,最后访问右子树。
因此,先将根结点进栈,在栈不空时循环:出栈p
,访问*p
结点,将其右孩子结点进栈,再将左孩子结点进栈。
void preOrder(BtTree root){
initStack(&S);
p = root;
if(root != NULL){
Push(&S,p); //根节点进栈
while(!StackEmpty(S)){ //栈不空时循环
Pop(&S, p); //出栈并访问该节点
Visit(p->data);
if(p->rchild != NULL){
Push(&S,p)->rchild); //右孩子进栈
} else if(p->lchild != NULL){ //左孩子进栈
Push(&S, lchild);
}
}
}
}