詳解二叉樹的遞歸遍歷與非遞歸遍歷——(二)

非遞歸遍歷

   上一邊文章中,咱們談到了二叉樹的遞歸遍歷,也是十分的簡單哈,這回又繼續將非遞歸遍歷寫一下。從前序開始扯吧,哈哈!!!
先給出存儲結構:

typedef struct Node{
      int data; //保存節點中的值
      struct BTNode *lchild; //左孩子指針
      struct BTNode *rchild; //右孩子指針
}BTNode,*BiTree;

1.前序遍歷

  • 先訪問根結點;
  • 然後在訪問左子樹;
  • 最後訪問右子樹;

前序遍歷大體流程:

  1. 首先,掃描並訪問根結點1,(這裏用到一個保存節點的棧)然後將根結點保存到棧中,並將根節點指向他的左孩子2(如果存在);
  2. 然後開始循環,每一次循環都訪問改結點,然後將該結點壓入棧中,一直循環到某一時刻的左孩子爲NULL的 時候,也就是訪問到了A結點的左子樹中的最深層左孩子7;
  3. 則進行下一步,將結點指針指向該7的右孩子(如果存在),先訪問該結點,然後繼續訪問該右孩子的左子樹,重複第一步,就會一直自底向上的將1結點的左子樹訪問完畢;
  4. 最後,當回到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

總的來說,二叉樹的非遞歸遍歷相對遞歸遍歷是難理解一點的,但是非遞歸遍歷算法的執行效率是要高於遞歸算法的。

    可能我有的地方說的不夠詳細、易懂,如果還是不大好理解;大家可以留言,我會盡我所能的去解釋,或者是可以自己模擬一下,畫一個二叉樹,然後對着代碼進行模擬一下流程,相信會很容易理解的。

希望大家無論遇到什麼算法,都要靜下心來,去想,去實踐,去搜索,只有這樣,纔有提高。一起加油!!!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章