非遞歸遍歷
上一邊文章中,咱們談到了二叉樹的遞歸遍歷,也是十分的簡單哈,這回又繼續將非遞歸遍歷寫一下。從前序開始扯吧,哈哈!!!
先給出存儲結構:
typedef struct Node{
int data; //保存節點中的值
struct BTNode *lchild; //左孩子指針
struct BTNode *rchild; //右孩子指針
}BTNode,*BiTree;
1.前序遍歷
- 先訪問根結點;
- 然後在訪問左子樹;
- 最後訪問右子樹;
前序遍歷大體流程:
- 首先,掃描並訪問根結點1,(這裏用到一個保存節點的棧)然後將根結點保存到棧中,並將根節點指向他的左孩子2(如果存在);
- 然後開始循環,每一次循環都訪問改結點,然後將該結點壓入棧中,一直循環到某一時刻的左孩子爲NULL的 時候,也就是訪問到了A結點的左子樹中的最深層左孩子7;
- 則進行下一步,將結點指針指向該7的右孩子(如果存在),先訪問該結點,然後繼續訪問該右孩子的左子樹,重複第一步,就會一直自底向上的將1結點的左子樹訪問完畢;
- 最後,當回到1結點時,根結點1和他的左子樹已經全部遍歷完成,則開始遍歷右子樹了。流程和第3步類似。
代碼重現:
void PreOrder(BiTree T){
InitStack(S); //初始化棧
BiTree p = T; //將P指向T,作爲遍歷指針
while(p || !StackEmpty(S)){//p不爲空或者棧不爲空時候,繼續遍歷
if(p){
visit(p); //訪問根結點
Push(S,p); //將根節點壓入棧中
p = p->lchild; //往左子樹走
}//if
//p爲空跳到else表示已經訪問到了左子樹的最底層的那個左孩子;
//現在要做的是從下往上一次打印左右節點(實際上只有右節點了);
else{
Pop(S,p);//彈出棧頂元素,訪問
visit(p);
p = p->rchild; //往右子樹走
}
}//while
}//PreOrder
可以說從整個流程比較簡單。
2.中序遍歷
中序遍歷可以說是三種非遞歸遍歷最簡單的一種的。
- 先訪問左子樹;
- 然後在訪問根結點;
- 最後訪問右子樹;
中序遍歷大體流程:
1. 先掃描根結點(並非訪問根結點)的所有的左結點,並將它們一一入棧,直到掃描到最深層的左結點,即停止該操作。進而將棧頂元素彈出,這裏設爲*p,(顯然 *p沒有左孩子結點或左孩子結點均已經被訪問郭),訪問它。然後掃描該結點的右孩子結點,將其進棧,在掃描該右孩子的所有結點並一一進棧,如此繼續,直到棧爲空位置。
void InOrder(BiTree T){
InitStack(S); //初始化棧
BiTree p = T; //p作爲遍歷指針
while(p || !EmptStack(S)){ //只要p不爲空,且棧不爲空,就繼續掃描
if(p){ //每次遇到非空二叉樹,就向左走
push(S,p);
p=p->lchild;
}else{ //跟指針退棧,訪問根結點,遍歷右子樹
Pop(S,p); //退棧
visit(p); //訪問根結點
p=p->rchild; //在向右子樹走
}
}
}
3.後序遍歷
都說後續非遞歸是最難的。可是換個角度想想:你把最難的都掌握了,你就肯定不會比別人差,可能寫的不好,但是請看下去。
直接談後序遍歷的思想吧!
————後序遍歷就是先訪問左子樹,在訪問右子樹,最後訪問根結點,當用堆棧來存儲結點時,應該分清返回根節點時是從左子樹訪問返回的還是從右子樹返回的,
①因爲如果是訪問了左子樹返回的,那麼久還需要去訪問右子樹;
②如果是從右子樹返回的,則可以直接訪問根結點了,這一點需要特別注意。
所以這裏藉助輔助指針 preNode,指向醉經訪問過的結點。也可在結點中增加一個標誌域,記錄是否已被訪問。
void PostOrder(BiTree T){
InitStack(S);
p=T;
preNode=NULL;
while(p && !EmptyStack(S)){
if(p){ //走到最左邊的結點
push(S,p);
p = p->lchild;
}else{ //向右
GetTop(S,p); //去棧頂節點
if(p->rchild && p->rchild != preNode){
//若右子樹存在,且未被訪問過
p = p->rchild; //轉向右子樹
push(S,p); //壓入棧
p = p->lchild; //再走到最左
}//if
else{//否則,直接訪問根結點了
pop(S,p); //將該結點彈出
visit(p); //訪問改結點
preNode = p; //記錄最近訪問過的結點
p = NULL; //結點訪問完後,重置指針p
}//else
}//else向右結尾
}//while
}//PostOrder
總的來說,二叉樹的非遞歸遍歷相對遞歸遍歷是難理解一點的,但是非遞歸遍歷算法的執行效率是要高於遞歸算法的。
可能我有的地方說的不夠詳細、易懂,如果還是不大好理解;大家可以留言,我會盡我所能的去解釋,或者是可以自己模擬一下,畫一個二叉樹,然後對着代碼進行模擬一下流程,相信會很容易理解的。
希望大家無論遇到什麼算法,都要靜下心來,去想,去實踐,去搜索,只有這樣,纔有提高。一起加油!!!