二叉樹的遞歸遍歷與非遞歸算法實現

       經過遞歸算法與非遞歸算法的比較,更好地理解各自的特色。非遞歸其實就是調用棧的基本操做,進棧,出棧等。
這裏面也正好複習了下棧的基本算法的實現。
 
棧和隊列的實如今個人前一篇博文裏。
 
基本數據結構
typedef  struct  BiNode
{
       char  data;   //此處,二叉樹中節點值類型爲字符型
       struct  BiNode *lchild,*rchild;     //左右孩子節點
}BiNode,*BiTree;   
 
二叉樹的建立
先申請根節點的空間,而後賦值,而後分別遞歸創建其左子樹和右子樹
//按照先序序列輸入構建一棵二叉樹
 1 //按照先序序列輸入構建一棵二叉樹
 2 void Create(BiTree &T)
 3 {
 4     char ch;
 5     scanf("%c",&ch);
 6     if( '#' == ch )
 7     {
 8         T = NULL;
 9     }
10     else
11     {
12         T=(BiNode *)malloc(sizeof(BiNode));
13         T->data=ch;
14         Create(T->lchild);
15         Create(T->rchild);
16     }
17 }
View Code

 

二叉樹的遞歸遍歷算法node

//二叉樹的遍歷,三種順序的遞歸遍歷,其實就是訪問根節點的順序不一樣,這裏的訪問操做是打印結點,也能夠是對結點其餘的操做
 
 1 //二叉樹的先序遍歷
 2 
 3 void PreOrder(BiTree T)
 4 
 5 {
 6 
 7       if(T)
 8 
 9      {
10 
11            printf( "%c ",T->data);
12 
13            PreOrder(T->lchild);
14 
15            PreOrder(T->rchild);
16 
17      }
18 
19 }
20 
21 //二叉樹的中序遍歷
22 
23 void InOrder(BiTree T)
24 
25 {
26 
27       if(T)
28 
29      {
30 
31            InOrder(T->lchild);
32 
33            printf( "%c ",T->data);
34 
35            InOrder(T->rchild);
36 
37      }
38 
39 }
40 
41  
42 
43 //二叉樹的後序遍歷
44 
45 void PostOrder(BiTree T)
46 
47 {
48 
49       if(T)
50 
51      {
52 
53            PostOrder(T->lchild);
54 
55            PostOrder(T->rchild);
56 
57            printf( "%c ",T->data);
58 
59      }
60 
61 }
View Code

 

 
二叉樹的非遞歸遍歷算法
 
二叉樹先序遍歷的非遞歸算法
/* 設置一個存放結點指針的棧 S ,從根結點開始,每訪問一結點後,按先序規則走左子樹,若右子樹存在,

則將右子樹指針進棧,以便之後能正確地返回到該右子樹上進行遍歷訪問。

*/

void PreTraverse(BiTree T)

{

     BiNode *p;

     Stack S;

     S=InitStack();

      if (T)

     {

           Push(S,T);  // 根結點指針進棧

            while (!StackEmpty(S))

           {

                p=Pop(S);  // 出棧,棧頂元素賦給 p

                 while (p)

                {

                     printf( "%c\t" ,p->data);  // 訪問 p結點

                      if (p->rchild)

                           Push(S,p->rchild);  // 右子樹存在時,進棧

                     p=p->lchild;            // 繼續沿着左子樹往下走

                }

           }

     }

}

/*web

說明:內部循環是從  p  結點出發一直走到最左,走的過程當中保存了每個右子樹的地址,
(由於右子樹尚未被訪問)並且是先進後出的,即先保存的比後保存的更先被用做返回地址,
因此是用棧。外循環正好是當內部循環不下去的時候,退一棧的情形。即換成他的右子樹。
*/
 
//  二叉樹的中序遍歷的非遞歸算法
/*

同前序遍歷 , 棧S 存放結點指針。對每棵子樹 ( 開始是整棵二叉樹 ),沿左找到該子樹在中序下的第一結點

( 但尋找路徑上的每一個結點指針要進棧 ), 訪問之; 而後遍歷該結點的右子樹 ,

又尋找該子樹在中序下的第一結點, .. …直到棧S 空爲止。

*/


void InTraverse(BiTree T)

{

     BiNode *p;

     Stack S;

     S=InitStack();

     Push(S,T);  // 根結點指針進棧

      while (!StackEmpty(S))

     {

            while ((p=GetsTop(S))&&p)  //取棧頂元素且存在,賦給 p

                Push(S,p->lchild);    //p 的左子樹進棧

           p=Pop(S);               // 去掉最後的空指針

            if (!StackEmpty(S))

           {

                p=Pop(S);       // 彈出棧頂元素,賦給 p

                printf( "%c\t" ,p->data);   // 訪問 p結點

                Push(S,p->rchild);     // 右子樹進棧,而後遍歷右子樹

           }

     }

}

 /*算法

說明:和前序不同,這裏的棧保存的是根結點的地址(由於中序遍歷先訪問左子樹,
而根結點沒有被訪問到。而前序遍歷不同,他一開始就訪問根結點,
因此他不保存根結點的地址而是保存右子樹的地址,由於右子樹尚未被訪問。
總之,用棧就是爲了幫咱們保存尚未被訪問的地址,以便未來咱們能找到返回的地址)。
*/
 
/*  後序遍歷二叉樹的非遞歸算法   */
/* 對一個結點是否能訪問,要看他的左、右子樹是否遍歷完, */

/* 因此每個結點對應一個標誌位 -tag 。tag=0 ,表示該結點暫不能訪問; tag=1 ,表示該結點能夠訪問 */

/* 實際上是區分此次返回是遍歷完左子樹返回的仍是遍歷完右子樹返回的,若是是左子樹返回的那麼就不能訪問根結點, */

/* 若是是右子樹返回的就能訪問根結點。當搜索到某 P 結點時,先要遍歷其左子樹,於是將結點地址 P 及tag=0 進棧; */

/* 當P 結點的左子樹遍歷完以後,再遍歷其右子樹,又將地址 P 及tag=1 進棧;當 P結點右子樹遍歷完以後( tag=1 ),即可對 P結點進行訪問 */

void PostTraverse(BiTree T)

{

      int tag;

     BiNode *p;

     Stacks S;

     SNode sdata;

     S=InitStacks();

     p=T;

      while (p||!StacksEmpty(S))

     {

            while (p)

           {

                sdata.q=p;

                sdata.tag=0;

                Pushs(S,&sdata);   //(p,0) 進棧

                p=p->lchild;      // 遍歷p 之左子樹

           }

           sdata=*Pops(S);  // 退棧

           p=sdata.q;     // 取指針

           tag=sdata.tag; // 狀態位

            if (tag==0)     //從左子樹返回時,根的 tag=0

           {

                sdata.q=p;

                sdata.tag=1;      // 這時要進入根的右子樹了,因此根的 tag=1 ,下次碰到根時就能夠訪問了

                Pushs(S,&sdata);   //(p,1) 進棧,根還得進一次棧

                p=p->rchild;     // 遍歷右子樹

           }

            else           //tag=1,這是說明了右子樹訪問完了返回,因此此次要對根進行訪問了

           {

                printf( "%c\t" ,p->data);

                p=NULL;

           }

     }

}

 

二叉樹的層次遍歷
// 二叉樹的層次遍歷

void LevelTraverse(BiTree T)

{

     BiNode *p;

     LinkQueue *Q;

     InitQueue(Q);

     EnQueue(Q,T);

      while (!QueueEmpty(Q))

     {

           p=DeQueue(Q);

           printf( "%c\t" ,p->data);

            if (p->lchild!=NULL)

                EnQueue(Q,p->lchild);

            if (p->rchild!=NULL)

                EnQueue(Q,p->rchild);

     }

}

 

下面是完整代碼:
  1 #include <stdio.h>
  2 
  3 #include <stdlib.h>
  4 
  5 #include <string.h>
  6 
  7 #include "treenode.h"
  8 
  9 #include "mystack.h"
 10 
 11 #include "myqueue.h"
 12 
 13 #include "newstack.h"
 14 
 15  
 16 
 17 //按照先序序列輸入構建一棵二叉樹
 18 
 19 void Create(BiTree &T)
 20 
 21 {
 22 
 23       char ch;
 24 
 25      scanf( "%c",&ch);
 26 
 27       if( '#' == ch )
 28 
 29      {
 30 
 31            T = NULL;
 32 
 33      }
 34 
 35       else
 36 
 37      {
 38 
 39            T=(BiNode *)malloc( sizeof(BiNode));
 40 
 41            T->data=ch;
 42 
 43            Create(T->lchild);
 44 
 45            Create(T->rchild);
 46 
 47      }
 48 
 49 }
 50 
 51  
 52 
 53 //二叉樹的先序遍歷
 54 
 55 void PreOrder(BiTree T)
 56 
 57 {
 58 
 59       if(T)
 60 
 61      {
 62 
 63            printf( "%c ",T->data);
 64 
 65            PreOrder(T->lchild);
 66 
 67            PreOrder(T->rchild);
 68 
 69      }
 70 
 71 }
 72 
 73  
 74 
 75 //二叉樹先序遍歷的非遞歸算法
 76 
 77 void PreTraverse(BiTree T)
 78 
 79 {
 80 
 81      BiNode *p;
 82 
 83      Stack S;
 84 
 85      S=InitStack();
 86 
 87       if(T)
 88 
 89      {
 90 
 91            Push(S,T);  //根結點指針進棧
 92 
 93             while(!StackEmpty(S))
 94 
 95            {
 96 
 97                 p=Pop(S);  //出棧,棧頂元素賦給p
 98 
 99                  while(p)
100 
101                 {
102 
103                      printf( "%c\t",p->data);  // 訪問p結點
104 
105                       if(p->rchild)
106 
107                            Push(S,p->rchild);  //右子樹存在時,進棧
108 
109                      p=p->lchild;            //繼續沿着左子樹往下走
110 
111                 }
112 
113            }
114 
115      }
116 
117 }
118 
119  
120 
121 //二叉樹的中序遍歷
122 
123 void InOrder(BiTree T)
124 
125 {
126 
127       if(T)
128 
129      {
130 
131            InOrder(T->lchild);
132 
133            printf( "%c ",T->data);
134 
135            InOrder(T->rchild);
136 
137      }
138 
139 }
140 
141  
142 
143 //二叉樹的中序遍歷的非遞歸算法
144 
145 void InTraverse(BiTree T)
146 
147 {
148 
149      BiNode *p;
150 
151      Stack S;
152 
153      S=InitStack();
154 
155      Push(S,T);  //根結點指針進棧
156 
157       while(!StackEmpty(S))
158 
159      {
160 
161             while((p=GetsTop(S))&&p)  //取棧頂元素且存在,賦給 p
162 
163                 Push(S,p->lchild);    //p的左子樹進棧
164 
165            p=Pop(S);               //去掉最後的空指針
166 
167             if(!StackEmpty(S))
168 
169            {
170 
171                 p=Pop(S);       //彈出棧頂元素,賦給p
172 
173                 printf( "%c\t",p->data);   // 訪問p結點
174 
175                 Push(S,p->rchild);     //右子樹進棧,而後遍歷右子樹
176 
177            }
178 
179      }
180 
181 }
182 
183  
184 
185 //二叉樹的後序遍歷
186 
187 void PostOrder(BiTree T)
188 
189 {
190 
191       if(T)
192 
193      {
194 
195            PostOrder(T->lchild);
196 
197            PostOrder(T->rchild);
198 
199            printf( "%c ",T->data);
200 
201      }
202 
203 }
204 
205  
206 
207 void PostTraverse(BiTree T)
208 
209 {
210 
211       int tag;
212 
213      BiNode *p;
214 
215      Stacks S;
216 
217      SNode sdata;
218 
219      S=InitStacks();
220 
221      p=T;
222 
223       while(p||!StacksEmpty(S))
224 
225      {
226 
227             while(p)
228 
229            {
230 
231                 sdata.q=p;
232 
233                 sdata.tag=0;
234 
235                 Pushs(S,&sdata);   //(p,0)進棧
236 
237                 p=p->lchild;      //遍歷p 之左子樹
238 
239            }
240 
241            sdata=*Pops(S);  //退棧
242 
243            p=sdata.q;     //取指針
244 
245            tag=sdata.tag; //狀態位
246 
247             if(tag==0)     //從左子樹返回時,根的 tag=0
248 
249            {
250 
251                 sdata.q=p;
252 
253                 sdata.tag=1;      //這時要進入根的右子樹了,因此根的 tag=1,下次碰到根時就能夠訪問了
254 
255                 Pushs(S,&sdata);   //(p,1)進棧,根還得進一次棧
256 
257                 p=p->rchild;     //遍歷右子樹
258 
259            }
260 
261             else          //tag=1,這是說明了右子樹訪問完了返回,因此此次要對根進行訪問了
262 
263            {
264 
265                 printf( "%c\t",p->data);
266 
267                 p=NULL;
268 
269            }
270 
271      }
272 
273 }
274 
275  
276 
277 //二叉樹的層次遍歷
278 
279 void LevelTraverse(BiTree T)
280 
281 {
282 
283      BiNode *p;
284 
285      LinkQueue *Q;
286 
287      InitQueue(Q);
288 
289      EnQueue(Q,T);
290 
291       while(!QueueEmpty(Q))
292 
293      {
294 
295            p=DeQueue(Q);
296 
297            printf( "%c\t",p->data);
298 
299             if(p->lchild!=NULL)
300 
301                 EnQueue(Q,p->lchild);
302 
303             if(p->rchild!=NULL)
304 
305                 EnQueue(Q,p->rchild);
306 
307      }
308 
309 }
310 
311  
312 
313 int main()
314 
315 {
316 
317      BiTree T;
318 
319      Create(T);
320 
321      PostOrder(T);
322 
323      printf( "\n");
324 
325      LevelTraverse(T);
326 
327      printf( "\n");
328 
329      PostTraverse(T);
330 
331      printf( "\n");
332 
333  
334 
335       return 0;
336 
337 }
View Code

 

/* 測試數據 */
/*   分別是構建二叉樹的輸入數據以及先序、中序、後序、層次遍歷序列:
ABE##C#D##F#GH#I##J##
ABECDFGHIJ
EBCDAFHIGJ
EDCBIGJGFA
ABFECGDHJI
 */
圖畫得有點醜,見笑了。
相關文章
相關標籤/搜索