日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

二叉树的三种遍历(递归与非递归) + 层次遍历

發布時間:2023/12/10 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 二叉树的三种遍历(递归与非递归) + 层次遍历 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

<轉載于? >>> >

二叉樹是一種非常重要的數據結構,很多其他數據機構都是基于二叉樹的基礎演變過來的。二叉樹有前、中、后三種遍歷方式,因為樹的本身就是用遞歸定義的,因此采用遞歸的方法實現三種遍歷,不僅代碼簡潔且容易理解,但其開銷也比較大,而若采用非遞歸方法實現三種遍歷,則要用棧來模擬實現(遞歸也是用棧實現的)。下面先簡要介紹三種遍歷方式的遞歸實現,再詳細介紹三種遍歷方式的非遞歸實現。

?

一、三種遍歷方式的遞歸實現(比較簡單,這里不詳細講解)

?

1、先序遍歷——按照“根節點-左孩子-右孩子”的順序進行訪問。

1 void pre_traverse(BTree pTree) 2 { 3 if(pTree) 4 { 5 printf("%c ",pTree->data); 6 if(pTree->pLchild) 7 pre_traverse(pTree->pLchild); 8 if(pTree->pRchild) 9 pre_traverse(pTree->pRchild); 10 } 11 }

?2、中序遍歷——按照“左孩子-根節點-右孩子”的順序進行訪問。

1 void in_traverse(BTree pTree) 2 { 3 if(pTree) 4 { 5 if(pTree->pLchild) 6 in_traverse(pTree->pLchild); 7 printf("%c ",pTree->data); 8 if(pTree->pRchild) 9 in_traverse(pTree->pRchild); 10 } 11 }

?3、后序遍歷——按照“左孩子-右孩子-根節點”的順序進行訪問。

1 void beh_traverse(BTree pTree) 2 { 3 if(pTree) 4 { 5 if(pTree->pLchild) 6 beh_traverse(pTree->pLchild); 7 if(pTree->pRchild) 8 beh_traverse(pTree->pRchild); 9 printf("%c ",pTree->data); 10 }

二、三種遍歷方式的非遞歸實現

??? 為了便于理解,這里以下圖的二叉樹為例,分析二叉樹的三種遍歷方式的實現過程。

?

1、前序遍歷的非遞歸實現?


根據先序遍歷的順序,先訪問根節點,再訪問左子樹,后訪問右子樹,而對于每個子樹來說,又按照同樣的訪問順序進行遍歷,上圖的先序遍歷順序為:ABDECF。非遞歸的實現思路如下:

對于任一節點P,

1)輸出節點P,然后將其入棧,再看P的左孩子是否為空;

2)若P的左孩子不為空,則置P的左孩子為當前節點,重復1)的操作;

3)若P的左孩子為空,則將棧頂節點出棧,但不輸出,并將出棧節點的右孩子置為當前節點,看其是否為空;

4)若不為空,則循環至1)操作;

5)如果為空,則繼續出棧,但不輸出,同時將出棧節點的右孩子置為當前節點,看其是否為空,重復4)和5)操作;

6)直到當前節點P為NULL并且棧空,遍歷結束。

???
?? 下面以上圖為例詳細分析其先序遍歷的非遞歸實現過程:


首先,從根節點A開始,根據操作1),輸出A,并將其入棧,由于A的左孩子不為空,根據操作2),將B置為當前節點,再根據操作1),將B輸出,并將其入棧,由于B的左孩子也不為空,根據操作2),將D置為當前節點,再根據操作1),輸出D,并將其入棧,此時輸出序列為ABD;

由于D的左孩子為空,根據操作3),將棧頂節點D出棧,但不輸出,并將其右孩子置為當前節點;

由于D的右孩子為空,根據操作5),繼續將棧頂節點B出棧,但不輸出,并將其右孩子置為當前節點;

由于B的右孩子E不為空,根據操作1),輸出E,并將其入棧,此時輸出序列為:ABDE;

由于E的左孩子為空,根據操作3),將棧頂節點E出棧,但不輸出,并將其右孩子置為當前節點;

由于E的右孩子為空,根據操作5),繼續將棧頂節點A出棧,但不輸出,并將其右孩子置為當前節點;

由于A的右孩子C不為空,根據操作1),輸出C,并將其入棧,此時輸出序列為:ABDEC;

由于A的左孩子F不為空,根據操作2),則將F置為當前節點,再根據操作1),輸出F,并將其入棧,此時輸出序列為:ABDECF;

由于F的左孩子為空,根據操作3),將棧頂節點F出棧,但不輸出,并將其右孩子置為當前節點;

由于F的右孩子為空,根據操作5),繼續將棧頂元素C出棧,但不輸出,并將其右孩子置為當前節點;

此時棧空,且C的右孩子為NULL,因此遍歷結束。

?

根據以上思路,前序遍歷的非遞歸實現代碼如下:

1 void pre_traverse(BTree pTree) 2 { 3 PSTACK stack = create_stack(); //創建一個空棧 4 BTree node_pop; //用來保存出棧節點 5 BTree pCur = pTree; //定義用來指向當前訪問的節點的指針 6 7 //直到當前節點pCur為NULL且棧空時,循環結束 8 while(pCur || !is_empty(stack)) 9 { 10 //從根節點開始,輸出當前節點,并將其入棧, 11 //同時置其左孩子為當前節點,直至其沒有左孩子,及當前節點為NULL 12 printf("%c ", pCur->data); 13 push_stack(stack,pCur); 14 pCur = pCur->pLchild; 15 //如果當前節點pCur為NULL且棧不空,則將棧頂節點出棧, 16 //同時置其右孩子為當前節點,循環判斷,直至pCur不為空 17 while(!pCur && !is_empty(stack)) 18 { 19 pCur = getTop(stack); 20 pop_stack(stack,&node_pop); 21 pCur = pCur->pRchild; 22 } 23 } 24 }

2、中序遍歷的非遞歸實現

根據中序遍歷的順序,先訪問左子樹,再訪問根節點,后訪問右子樹,而對于每個子樹來說,又按照同樣的訪問順序進行遍歷,上圖的中序遍歷順序為:DBEAFC。非遞歸的實現思路如下:

對于任一節點P,

1)若P的左孩子不為空,則將P入棧并將P的左孩子置為當前節點,然后再對當前節點進行相同的處理;

2)若P的左孩子為空,則輸出P節點,而后將P的右孩子置為當前節點,看其是否為空;

3)若不為空,則重復1)和2)的操作;

4)若為空,則執行出棧操作,輸出棧頂節點,并將出棧的節點的右孩子置為當前節點,看起是否為空,重復3)和4)的操作;

5)直到當前節點P為NULL并且棧為空,則遍歷結束。

?

?? 下面以上圖為例詳細分析其中序遍歷的非遞歸實現過程:

首先,從根節點A開始,A的左孩子不為空,根據操作1)將A入棧,接著將B置為當前節點,B的左孩子也不為空,根據操作1),將B也入棧,接著將D置為當前節點,由于D的左子樹為空,根據操作2),輸出D;

由于D的右孩子也為空,根據操作4),執行出棧操作,將棧頂結點B出棧,并將B置為當前節點,此時輸出序列為DB;

由于B的右孩子不為空,根據操作3),將其右孩子E置為當前節點,由于E的左孩子為空,根據操作1),輸出E,此時輸出序列為DBE;

由于E的右孩子為空,根據操作4),執行出棧操作,將棧頂節點A出棧,并將節點A置為當前節點,此時輸出序列為DBEA;

此時棧為空,但當前節點A的右孩子并不為NULL,繼續執行,由于A的右孩子不為空,根據操作3),將其右孩子C置為當前節點,由于C的左孩子不為空,根據操作1),將C入棧,將其左孩子F置為當前節點,由于F的左孩子為空,根據操作2),輸出F,此時輸出序列為:DBEAF;

由于F的右孩子也為空,根據操作4),執行出棧操作,將棧頂元素C出棧,并將其置為當前節點,此時的輸出序列為:DBEAFC;

由于C的右孩子為NULL,且此時棧空,根據操作5),遍歷結束。

?

????根據以上思路,中序遍歷的非遞歸實現代碼如下:

1 void in_traverse(BTree pTree) 2 { 3 PSTACK stack = create_stack(); //創建一個空棧 4 BTree node_pop; //用來保存出棧節點 5 BTree pCur = pTree; //定義指向當前訪問的節點的指針 6 7 //直到當前節點pCur為NULL且棧空時,循環結束 8 while(pCur || !is_empty(stack)) 9 { 10 if(pCur->pLchild) 11 { 12 //如果pCur的左孩子不為空,則將其入棧,并置其左孩子為當前節點 13 push_stack(stack,pCur); 14 pCur = pCur->pLchild; 15 } 16 else 17 { 18 //如果pCur的左孩子為空,則輸出pCur節點,并將其右孩子設為當前節點,看其是否為空 19 printf("%c ", pCur->data); 20 pCur = pCur->pRchild; 21 //如果為空,且棧不空,則將棧頂節點出棧,并輸出該節點, 22 //同時將它的右孩子設為當前節點,繼續判斷,直到當前節點不為空 23 while(!pCur && !is_empty(stack)) 24 { 25 pCur = getTop(stack); 26 printf("%c ",pCur->data); 27 pop_stack(stack,&node_pop); 28 pCur = pCur->pRchild; 29 } 30 } 31 } 32 }

3、后序遍歷的非遞歸實現

根據后序遍歷的順序,先訪問左子樹,再訪問右子樹,后訪問根節點,而對于每個子樹來說,又按照同樣的訪問順序進行遍歷,上圖的后序遍歷順序為:DEBFCA。后序遍歷的非遞歸的實現相對來說要難一些,要保證根節點在左子樹和右子樹被訪問后才能訪問,思路如下:

對于任一節點P,

1)先將節點P入棧;

2)若P不存在左孩子和右孩子,或者P存在左孩子或右孩子,但左右孩子已經被輸出,則可以直接輸出節點P,并將其出棧,將出棧節點P標記為上一個輸出的節點,再將此時的棧頂結點設為當前節點;

3)若不滿足2)中的條件,則將P的右孩子和左孩子依次入棧,當前節點重新置為棧頂結點,之后重復操作2);

4)直到棧空,遍歷結束。

?

?? 下面以上圖為例詳細分析其后序遍歷的非遞歸實現過程:

首先,設置兩個指針:Cur指針指向當前訪問的節點,它一直指向棧頂節點,每次出棧一個節點后,將其重新置為棧頂結點,Pre節點指向上一個訪問的節點;

Cur首先指向根節點A,Pre先設為NULL,由于A存在左孩子和右孩子,根據操作3),先將右孩子C入棧,再將左孩子B入棧,Cur改為指向棧頂結點B;

由于B的也有左孩子和右孩子,根據操作3),將E、D依次入棧,Cur改為指向棧頂結點D;

由于D沒有左孩子,也沒有右孩子,根據操作2),直接輸出D,并將其出棧,將Pre指向D,Cur指向棧頂結點E,此時輸出序列為:D;

由于E也沒有左右孩子,根據操作2),輸出E,并將其出棧,將Pre指向E,Cur指向棧頂結點B,此時輸出序列為:DE;

由于B的左右孩子已經被輸出,即滿足條件Pre==Cur->lchild或Pre==Cur->rchild,根據操作2),輸出B,并將其出棧,將Pre指向B,Cur指向棧頂結點C,此時輸出序列為:DEB;

由于C有左孩子,根據操作3),將其入棧,Cur指向棧頂節點F;

由于F沒有左右孩子,根據操作2),輸出F,并將其出棧,將Pre指向F,Cur指向棧頂結點C,此時輸出序列為:DEBF;

由于C的左孩子已經被輸出,即滿足Pre==Cur->lchild,根據操作2),輸出C,并將其出棧,將Pre指向C,Cur指向棧頂結點A,此時輸出序列為:DEBFC;

由于A的左右孩子已經被輸出,根據操作2),輸出A,并將其出棧,此時輸出序列為:DEBFCA;

此時棧空,遍歷結束。


???根據以上思路,后序遍歷的非遞歸實現代碼如下:

1 void beh_traverse(BTree pTree) 2 { 3 PSTACK stack = create_stack(); //創建一個空棧 4 BTree node_pop; //用來保存出棧的節點 5 BTree pCur; //定義指針,指向當前節點 6 BTree pPre = NULL; //定義指針,指向上一各訪問的節點 7 8 //先將樹的根節點入棧 9 push_stack(stack,pTree); 10 //直到棧空時,結束循環 11 while(!is_empty(stack)) 12 { 13 pCur = getTop(stack); //當前節點置為棧頂節點 14 if((pCur->pLchild==NULL && pCur->pRchild==NULL) || 15 (pPre!=NULL && (pCur->pLchild==pPre || pCur->pRchild==pPre))) 16 { 17 //如果當前節點沒有左右孩子,或者有左孩子或有孩子,但已經被訪問輸出, 18 //則直接輸出該節點,將其出棧,將其設為上一個訪問的節點 19 printf("%c ", pCur->data); 20 pop_stack(stack,&node_pop); 21 pPre = pCur; 22 } 23 else 24 { 25 //如果不滿足上面兩種情況,則將其右孩子左孩子依次入棧 26 if(pCur->pRchild != NULL) 27 push_stack(stack,pCur->pRchild); 28 if(pCur->pLchild != NULL) 29 push_stack(stack,pCur->pLchild); 30 } 31 } 32 }

? ? 以上遍歷算法在VC上實現的輸出結果如下:

?

?

二叉樹的層序遍歷:

二叉樹的層序遍歷的實現還是比較簡單的,由于其層級的關系,很明顯要用到隊列來輔助實現,主要是從左向右,自上而下,依次將二叉樹的各節點入隊,這樣便可以保證輸出的順序是層序排列的。下面是算法的實現思想:

? ? 先將樹的根節點入隊,

? ? 如果隊列不空,則進入循環

? ? {

? ? ? 將隊首元素出隊,并輸出它;

? ? ? 如果該隊首元素有左孩子,則將其左孩子入隊;

? ? ? 如果該隊首元素有右孩子,則將其右孩子入隊

? ? }

? ? C語言代碼如下:

1 void LevelOrderTraverse(BiTree T,Status(*Visit)(TElemType)) 2 { 3 //Visit是對節點操作的應用函數, 4 //在這里,對每個數據元素調用函數Visit,也即是遍歷了該節點 5 SqQueue q; 6 QElemType p; 7 if(T) 8 { 9 InitQueue(&q); 10 EnQueue(&q,T); 11 while(!QueueEmpty(q)) 12 { 13 DeQueue(&q,&p); 14 Visit(p->data); 15 if(p->lchild!=NULL) EnQueue(&q,p->lchild); 16 if(p->rchild!=NULL) EnQueue(&q,p->rchild); 17 } 18 printf("/n"); 19 } 20 }

?

?

 

轉載于:https://www.cnblogs.com/00isok/p/9871574.html

總結

以上是生活随笔為你收集整理的二叉树的三种遍历(递归与非递归) + 层次遍历的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。