二叉樹是樹的特殊一種,具備以下特色:一、每一個結點最多有兩顆子樹,結點的度最大爲2。二、左子樹和右子樹是有順序的,次序不能顛倒。三、即便某結點只有一個子樹,也要區分左右子樹。spa
全部的結點都只有左子樹(左斜樹),或者只有右子樹(右斜樹)。這就是斜樹,應用較少code
全部的分支結點都存在左子樹和右子樹,而且全部的葉子結點都在同一層上,這樣就是滿二叉樹。就是完美元滿的意思,關鍵在於樹的平衡。blog
根據滿二叉樹的定義,獲得其特色爲:繼承
對一棵具備n個結點的二叉樹按層序排號,若是編號爲i的結點與一樣深度的滿二叉樹編號爲i結點在二叉樹中位置徹底相同,就是徹底二叉樹。滿二叉樹必須是徹底二叉樹,反過來不必定成立。遞歸
其中關鍵點是按層序編號,而後對應查找。get
在上圖中,樹1,按層次編號5結點沒有左子樹,有右子樹,10結點缺失。樹2因爲3結點沒有字數,是的6,7位置空擋了。樹3中結點5沒有子樹。it
上圖就是一個徹底二叉樹。class
結合徹底二叉樹定義獲得其特色:二叉樹
根據下圖加深理解,何時是徹底二叉樹。循環
一、在非空二叉樹的i層上,至多有2i-1個節點(i>=1)。經過概括法論證。
二、在深度爲K的二叉樹上最多有2k-1個結點(k>=1)。經過概括法論證。
三、對於任何一棵非空的二叉樹,若是葉節點個數爲n0,度數爲2的節點個數爲n2,則有: n0 = n2 + 1
在一棵二叉樹中,除了葉子結點(度爲0)以外,就剩下度爲2(n2)和1(n1)的結點了。則樹的結點總數爲T = n0+n1+n2;在二叉樹中結點總數爲T,而連線數爲T-1.因此有:n0+n1+n2-1 = 2*n2 +n1;最後獲得n0 = n2+1;
上圖中結點總數是10,n2爲4,n1爲1,n0爲5。
a、具備n的結點的徹底二叉樹的深度爲log2n+1.
滿二叉樹是徹底二叉樹,對於深度爲k的滿二叉樹中結點數量是2k-1 = n,徹底二叉樹結點數量確定最多2k-1,同時徹底二叉樹倒數第二層確定是滿的(倒數第一層有結點,那麼卻是第二層序號和滿二叉樹相同),因此徹底二叉樹的結點數最少大於少一層的滿二叉樹,爲2k-1-1。
根據上面推斷得出: 2k-1-1< n=<2k-1,由於結點數Nn爲整數那麼n<=2k-1能夠推出n<=2k ,n>2k-1-1能夠推出 n>=2k-1,因此2k-1<n<=2k 。便可得k-1<=log2n<k 而k做爲整數所以k=[log2n]+1。
b、若是有一顆有n個節點的徹底二叉樹的節點按層次序編號,對任一層的節點i(1<=i<=n)有
1.若是i=1,則節點是二叉樹的根,無雙親,若是i>1,則其雙親節點爲[i/2],向下取整
2.若是2i>n那麼節點i沒有左孩子,不然其左孩子爲2i
3.若是2i+1>n那麼節點沒有右孩子,不然右孩子爲2i+1
在上圖中驗證
第一條:
當i=1時,爲根節點。當i>1時,好比結點爲7,他的雙親就是7/2= 3;結點9雙親爲4.
第二條:
結點6,6*2 = 12>10,因此結點6無左孩子,是葉子結點。結點5,5*2 = 10,左孩子是10,結點4,爲8.
第三條:
結點5,2*5+1>10,沒有右孩子,結點4,則有右孩子。
二叉樹遍歷:從樹的根節點出發,按照某種次序依次訪問二叉樹中全部的結點,使得每一個結點被訪問僅且一次。
這裏有兩個關鍵詞:訪問和次序。
基本思想:先訪問根結點,再先序遍歷左子樹,最後再先序遍歷右子樹即根—左—右。
圖中前序遍歷結果是:1,2,4,5,7,8,3,6。
a/前序遞歸遍歷的代碼實現,以下所示
//前序遞歸遍歷 void PreOrderTraverse(BiTree t) { //注意跳出條件 if(t != NULL) { //注意訪問語句順序 printf("%c ", t->data); PreOrderTraverse(t->lchild); PreOrderTraverse(t->rchild); } }
前序非遞歸遍歷:
對於任一結點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; }
基本思想:先中序遍歷左子樹,而後再訪問根結點,最後再中序遍歷右子樹即左—根—右。
圖中中序遍歷結果是:4,2,7,8,5,1,3,6。
中序遍歷迭代代碼
//中序遞歸遍歷 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; }
基本思想:前後序遍歷左子樹,而後再後序遍歷右子樹,最後再訪問根結點即左—右—根。
圖中後序遍歷結果是:4,8,7,5,2,6,3,1。
後序遞歸遍歷代碼實現,以下所示。
//後序遞歸遍歷 void PostOrderTraverse(BiTree t) { if(t != NULL) { PostOrderTraverse(t->lchild); PostOrderTraverse(t->rchild); printf("%c ", t->data); } }
後序遍歷的非遞歸實現是三種遍歷方式中最難的一種。由於在後序遍歷中,要保證左孩子和右孩子都已被訪問,而且左孩子在右孩子以前訪問才能訪問根結點,這就爲流程控制帶來了難題。下面介紹一種思路。
要保證根結點在左孩子和右孩子訪問以後才能訪問,所以對於任一結點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; }
其實而二叉樹的創建就是二叉樹的遍歷,只不過將輸入內容改成創建結點而已,好比,利用前序遍歷創建二叉樹
//建立樹 //按前後次序輸入二叉樹中結點的值(一個字符),#表示空樹 //構造二叉鏈表表示的二叉樹 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; }