遍歷的概念
順着某一條搜索路徑巡訪二叉樹中的結點,使得每個結點均被訪問一次,而且僅被訪問一次。
“訪問”的含義可以很廣,如:輸出結點的信息等。
“遍歷”是任何類型均有的操作,對線性結構而言,只有一條搜索路徑(因爲每個結點均只有一個後繼),故不需要另加討論。而二叉樹是非線性結構,每個結點有兩個後繼,則存在如何遍歷即按什麼樣的搜索路徑進行遍歷的問題。
搜索路徑
對於“二叉樹”而言,可以有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);
}
}
}
}