二叉树(遍历、建立、深度)
1、二叉樹的深度遍歷
????????二叉樹的遍歷是指從根結點出發,按照某種次序依次訪問二叉樹的所有結點,使得每個結點被訪問一次且僅被訪問一次。?
????????對于二叉樹的深度遍歷,有前序遍歷二叉樹、中序遍歷二叉樹、后序遍歷二叉樹三種形式,下面分別進行學習和介紹。
1.1?二叉樹的前序遍歷
????????1)前序遞歸遍歷
? ? ????規則是若二叉樹為空,則空操作返回,否則先訪問根結點,然后前序遍歷左子樹,再前序遍歷右子樹。如下圖所示,遍歷的順序為ABDGHCEIF。
?
前序遞歸遍歷的代碼實現,如下所示。
//前序遞歸遍歷 void PreOrderTraverse(BiTree t) {if(t != NULL){printf("%c ", t->data);PreOrderTraverse(t->lchild);PreOrderTraverse(t->rchild);} }
2)前序非遞歸遍歷
????根據前序遍歷訪問的順序,優先訪問根結點,然后再分別訪問左孩子和右孩子。即對任一結點,其可看做是根結點,因此可以直接訪問,訪問完之后,若其左孩子不為空,按相同的規則訪問它的左子樹;當訪問其左子樹時,再訪問它的右子樹,因此其處理過程如下:
????????對于任一結點p:
????????a.?訪問結點p,并將結點p入棧;
????????b.?判斷結點p的左孩子是否為空,若為空,則取棧頂結點并進行出棧操作,并將棧頂結點的右孩子置為當前的結點p,循環置a;若不為空,則將p的左孩子置為當前結點p;
????????c.?直到p為空,并且棧為空,則遍歷結束。
????????前序非遞歸遍歷代碼實現,如下所示。?
//前序非遞歸遍歷 int NoPreOrderTraverse(BiTree t) {SqStack s;InitStack(&s);BiTree tmp = t;if(tmp == NULL){fprintf(stdout, "the tree is null.\n");return ERROR;}while((tmp != NULL) || (IsEmpty(&s) != 1)) {while(tmp != NULL){Push(&s, tmp);printf("%c ", tmp->data);tmp = tmp->lchild;}if(IsEmpty(&s) != 1){Pop(&s, &tmp);tmp = tmp->rchild;}}return OK; }1.2?中序遍歷二叉樹
????????1)中序遞歸遍歷
??????規則是若樹為空,則空操作返回,否則從根結點開始(注意這里并不是先訪問根結點),中序遍歷根結點的左子樹,然后是訪問根結點,最后中序遍歷右子樹。如下圖所示,遍歷的順序為:GDHBAEICF。
???
????中序遞歸遍歷代碼實現如下所示。
//中序遞歸遍歷 void InOrderTraverse(BiTree t) {if(t != NULL){InOrderTraverse(t->lchild);printf("%c ", t->data);InOrderTraverse(t->rchild);} }
?2)中序非遞歸遍歷
? ? 根據中序遍歷的順序,對于任一結點,優先訪問其左孩子,而左孩子結點又可以看做一個根結點,然后繼續訪問其左孩子結點,直到遇到左孩子結點為空的結點才停止訪問,然后按相同的規則訪問其右子樹。其處理過程如下:
? ? ? ?對于任一結點:
? ? ? ?a.?若其左孩子不為空,則將p入棧,并將p的左孩子設置為當前的p,然后對當前結點再進行相同的操作;
???????b.?若其左孩子為空,則取棧頂元素并進行出棧操作,訪問該棧頂結點,然后將當前的p置為棧頂結點的右孩子;
???????c.?直到p為空并且棧為空,則遍歷結束。
???????中序非遞歸遍歷代碼實現如下所示。
//中序非遞歸遍歷二叉樹 int NoInOrderTraverse(BiTree t) {SqStack s;InitStack(&s);BiTree tmp = t;if(tmp == NULL){fprintf(stderr, "the tree is null.\n");return ERROR;}while(tmp != NULL || (IsEmpty(&s) != 1)){while(tmp != NULL){Push(&s, tmp);tmp = tmp->lchild;}if(IsEmpty(&s) != 1){Pop(&s, &tmp);printf("%c ", tmp->data);tmp = tmp->rchild;}}return OK; }1.3?后序遍歷二叉樹
????????1)后序遞歸遍歷
???????規則是若樹為空,則空操作返回,否則從左到右先葉子后結點的方式遍歷訪問左右子樹,最后是訪問根結點。遍歷的順序為:GHDBIEFCA。
????????后序遞歸遍歷代碼實現,如下所示。
//后序遞歸遍歷 void PostOrderTraverse(BiTree t) {if(t != NULL){PostOrderTraverse(t->lchild);PostOrderTraverse(t->rchild);printf("%c ", t->data);} }2)后序非遞歸遍歷
? ? 后序遍歷的非遞歸實現是三種遍歷方式中最難的一種。因為在后序遍歷中,要保證左孩子和右孩子都已被訪問,并且左孩子在右孩子之前訪問才能訪問根結點,這就為流程控制帶來了難題。下面介紹一種思路。
?????要保證根結點在左孩子和右孩子訪問之后才能訪問,因此對于任一結點p,先將其入棧。若p不存在左孩子和右孩子,則可以直接訪問它,或者p存在左孩子或右孩子,但是其左孩子和右孩子都已經被訪問過了,則同樣可以直接訪問該結點。若非上述兩種情況,則將p的右孩子和左孩子依次入棧,這樣就保證了每次取棧頂元素的時候,左孩子在右孩子之前別訪問,左孩子和右孩子都在根結點前面被訪問。
????????后序非遞歸遍歷代碼實現,如下所示。
//后序非遞歸遍歷二叉樹 int NoPostOrderTraverse(BiTree t) {SqStack s;InitStack(&s);BiTree cur; //當前結點 BiTree pre = NULL; //前一次訪問的結點BiTree tmp;if(t == NULL){fprintf(stderr, "the tree is null.\n");return ERROR;}Push(&s, t);while(IsEmpty(&s) != 1){GetTop(&s, &cur);//if((cur->lchild == NULL && cur->rchild == NULL) || (pre != NULL && (pre == cur->lchild || pre == cur->rchild))){printf("%c ", cur->data); //如果當前結點沒有孩子結點或者孩子結點都已被訪問過Pop(&s, &tmp);pre = cur;}else{if(cur->rchild != NULL){Push(&s, cur->rchild);}if(cur->lchild != NULL){Push(&s, cur->lchild);}}}return OK; }2、二叉樹的廣度遍歷?
? ?廣度遍歷二叉樹(即層次遍歷)是用隊列來實現的,從二叉樹的第一層(根結點)開始,自上而下逐層遍歷;在同一層中,按照從左到右的順序對結點逐一訪問。如下圖所示,遍歷的順序為:ABCDEFGHI。
???
????按照從根結點到葉結點、從左子樹到右子樹的次序訪問二叉樹的結點,具體思路如下:
A.?初始化一個隊列,并把根結點入隊列;
B.?當隊列為非空時,循環執行步驟3到步驟5,否則執行步驟6;
C.?出隊列取得一個結點,訪問該結點;
D.?若該結點的左子樹為非空,則將該結點的左子樹入隊列;
E.?若該結點的右子樹為非空,則將該結點的右子樹入隊列;
F.?結束。
廣度遍歷二叉樹代碼實現,如下所示。
//層次遍歷二叉樹 - 廣度遍歷二叉樹 - 隊列 int TraverseBiTree(BiTree t) {LinkQueue q;InitQueue(&q);BiTree tmp = t;if(tmp == NULL){fprintf(stderr, "the tree is null.\n");return ERROR;}InsertQueue(&q, tmp);while(QueueIsEmpty(&q) != OK){DeQueue(&q, &tmp);printf("%c ", tmp->data);if(tmp->lchild != NULL){InsertQueue(&q, tmp->lchild);}if(tmp->rchild != NULL){InsertQueue(&q, tmp->rchild);}}return OK; }3、二叉樹的建立
? ?如果要在內存中建立一個如下左圖這樣的樹,wield能讓每個結點確認是否有左右孩子,我們對它進行擴展,變成如下右圖的樣子,也就是將二叉樹中的每個結點的空指針引出一個虛結點,其值為一個特定值,比如”#”,稱之為擴展二叉樹。擴展二叉樹就可以做到一個遍歷序列確定一棵二叉樹了。如前序遍歷序列為AB#D##C##。
? ? 有了這樣的準備,就可以看看如何生成一棵二叉樹了。假設二叉樹的結點均為一個字符,把剛才前序遍歷序列AB#D##C##用鍵盤挨個輸入,實現的算法如下所示。
二叉樹建立實現代碼一,如下所示。
//創建樹 //按先后次序輸入二叉樹中結點的值(一個字符),#表示空樹 //構造二叉鏈表表示的二叉樹 BiTree CreateTree(BiTree t) {char ch;scanf("%c", &ch);if(ch == '#'){t = NULL;}else{t = (BitNode *)malloc(sizeof(BitNode));if(t == NULL){fprintf(stderr, "malloc() error in CreateTree.\n");return;}t->data = ch; //生成根結點t->lchild = CreateTree(t->lchild); //構造左子樹t->rchild = CreateTree(t->rchild); //構造右子樹}return t; } 二叉樹建立實現代碼二,如下所示。//創建樹方法二 int CreateTree2(BiTree *t) {char ch;scanf("%c", &ch);if(ch == '#'){(*t) = NULL;}else{(*t) = (BiTree)malloc(sizeof(BitNode));if((*t) == NULL){fprintf(stderr, "malloc() error in CreateTree2.\n");return ERROR;}(*t)->data = ch;CreateTree2(&((*t)->lchild));CreateTree2(&((*t)->rchild));}return OK; }
其實建立二叉樹,也是利用了遞歸的原理。只不過在原來應該打印結點的地方,改成生成結點、給結點賦值的操作而已。因此,完全可以用中序或后序遍歷的方式實現二叉樹的建立,只不過代碼里生成結點和構造左右子樹的代碼順序交互一下即可。
4、二叉樹的深度
樹中結點的最大層次稱為樹的深度。對于二叉樹,求解樹的深度用以下兩種方法實現。即非遞歸和遞歸的方法實現。
遞歸求解二叉樹的深度實現代碼,如下所示。
//二叉樹的深度 - 遞歸 //返回值: 二叉樹的深度 int BiTreeDeep(BiTree t) {int dept = 0;if(t){int lchilddept = BiTreeDeep(t->lchild);int rchilddept = BiTreeDeep(t->rchild);dept = lchilddept >= rchilddept ? (lchilddept + 1) : (rchilddept + 1);}return dept; }對于非遞歸求解二叉樹的深度,這里采用了層次遍歷的原理,通過層次遍歷,找到二叉樹的最后一個結點。然后,根據該結點,尋找其雙親結點,即找到其上一層,此時深度dept加1,依次進行,直到根結點為止。 非遞歸求解二叉樹深度的實現,如下所示。
//返回二叉樹的深度 - 非遞歸 - 受層次遍歷二叉樹的影響 //返回值: 二叉樹的深度 int NoBiTreeDeep(BiTree t) {LinkQueue q;InitQueue(&q);BiTree tmp = t;if(tmp == NULL){return ERROR;}InsertQueue(&q, tmp);while(QueueIsEmpty(&q) != OK){DeQueue(&q, &tmp);//printf("%c ", tmp->data);if(tmp->lchild != NULL){InsertQueue(&q, tmp->lchild);}if(tmp->rchild != NULL){InsertQueue(&q, tmp->rchild);}}int deep = 0;BiTree m = tmp;BiTree n = t;while(m != n){InsertQueue(&q, n);while(QueueIsEmpty(&q) != OK){DeQueue(&q, &tmp);if(m == tmp->lchild || m == tmp->rchild){deep++;m = tmp;break;}if(tmp->lchild != NULL){InsertQueue(&q, tmp->lchild);}if(tmp->rchild != NULL){InsertQueue(&q, tmp->rchild);}}}return deep + 1; //深度從1開始 }
總結
以上是生活随笔為你收集整理的二叉树(遍历、建立、深度)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 二叉树的递归和非递归遍历
- 下一篇: 单链表的建立,插入和释放