二叉樹是在樹的基礎上對本身的結構做了更高的限制:
- 二叉樹本身是有序樹。
- 二叉樹中各結點的度最多是 2,可以是 0,1,2。

滿二叉樹和完全二叉樹
如果二叉樹中除了葉子結點,每個結點的度都為 2,那么此二叉樹為滿二叉樹。例如圖 1 就是一個滿二叉樹。
如果二叉樹除了最后一層外為滿二叉樹,最后一層的結點依次從左到右分布,此二叉樹為完全二叉樹。

圖 2(A)和(B)都是二叉樹,但圖 2(A)是完全二叉樹,(B)由於最后一層不符合從左往右依次分布的要求,所以不是完全二叉樹,只是一個普通的二叉樹。
二叉樹的性質
二叉樹有以下幾個性質:
- 二叉樹中,第 i 層最多有
2^(i-1)
個結點。 - 如果二叉樹的深度為 K,那么此二叉樹最多有
2^K - 1
個結點。 - 二叉樹中,終端結點數(葉子結點數)為 n0,度為 2 的結點數為 n2,則
n0 = n2 + 1
。
性質 3 的計算方法為:
對於一個二叉樹來說,除了度為 0 的葉子結點和度為 2 的結點,剩下的就是度為 1 的結點(設為 n1),那么總結點
n = n0 + n1 + n2
。
同時,對於每一個結點來說都是由其父結點分支表示的,假設樹中分枝數為 B,那么總結點數n = B + 1
。而分枝數是可以通過 n1 和 n2 表示的:B = n1 + 2 * n2
。
所以,n 用另外一種方式表示為:n=n1+2*n2+1
。
兩種方式得到的 n 值組成一個方程組,就可以得出n0 = n2 + 1
。
完全二叉樹特有的性質
n 個結點的完全二叉樹的深度為[log2n]+1
。
證明過程:
對於滿二叉樹來說,k層總的節點個數n = 2^k - 1;則k = log2 (n + 1); 同理,對於倒數第二層以上的滿二叉樹的節點個數為2^(k - 1) - 1,對於完全二叉樹來說,節點個數 2^(k - 1) - 1 < n <=2^k - 1, 由於n是整數,因此上式可以看成2^(k - 1) <= n < 2^k;故 k - 1 <= log2 n < k;因此,k = [log2 n](取下限) + 1;
[log2n]表示取小於log2n的最大整數。例如,[log24] = 2,而 [log25] 結果也是 2。
對於任意一個完全二叉樹來說,將含有的結點按照層次從左到右依次標號(如圖 2(A)),對於任意一個結點 i ,有以下幾個結論:
- 當 i > 1時,父親結點為結點 [i / 2] 。( i = 1時,表示的是根結點,無父親結點)
- 如果 2*i > n ,則結點 i 肯定沒有左孩子(為葉子結點);否則其左孩子是結點 2*i 。
- 如果 2*i +1 > n ,則結點 i 肯定沒有右孩子;否則右孩子是結點 2*i +1 。
二叉樹的存儲結構
二叉樹有兩種存儲結構:順序存儲結構和鏈式存儲結構。
順序存儲
借用數組將二叉樹中的數據元素存儲起來。此方式只適用於完全二叉樹,如果想存儲普通二叉樹,需要將普通二叉樹轉化為完全二叉樹。
使用數組存儲完全二叉樹時,從數組的起始地址開始,按層次順序從左往右依次存儲完全二叉樹中的結點。當提取時,根據完全二叉樹的第 2 條性質,可以將二叉樹進行還原。
例如,存儲圖 2(A)時,數組中存儲為:
根據完全二叉樹的第 2 條性質就可以根據數組中的數據重新搭建二叉樹的結構。
如果普通二叉樹也采取順序存儲的方式,就需要將其轉化成完全二叉樹,然后再存儲,例如:

轉化前 轉化后
圖 3 中,轉化后的二叉樹中,數據元素 0 表示此位置沒有數據。將轉化后的完全二叉樹按照層次並從左到右的次序存儲到數組中:
由此可見。深度為 K 且只有 K 個結點的單支樹(樹中不存在度為 2 的結點),需要 2K-1
的數組空間,浪費存儲空間。所以,順序存儲方式更適用於完全二叉樹。
鏈式存儲
采用鏈式存儲結構存儲二叉樹,就非常容易理解了。根據每個結點的結構,至少需要 3 部分組成:

圖5 二叉鏈表結點構成
圖 5 中,Lchild
代表指向左孩子的指針域;data
為數據域;Rchild
代表指向右孩子的指針域。使用此種結點構建的二叉樹稱為“二叉鏈表”。
結點結構代碼表示:

typedef struct BiTNode{ TElemType data;//數據域 struct BiTNode *lchild,*rchild;//左右孩子指針 }BiTNode,*BiTree;
如果程序中需要頻繁地訪問結點的父結點,就可以使用下面這種結點結構:

圖 6 三叉鏈表結點構成
圖 6 中,Lchild
指向左孩子;Rchild
指向右孩子;data
為數據域;parent
指向父結點。使用這種結構的結點創建的樹稱為“三叉鏈表”。
結點結構代碼表示:

typedef struct BiTNode{ TElemType data;//數據域 struct BiTNode *lchild,*rchild;//左右孩子指針 struct BiTNode *parent; }BiTNode,*BiTree;

圖7 單支樹示意圖
實現代碼(以二叉鏈表為例)

#include <stdio.h> #include <stdlib.h> #define TElemType int typedef struct BiTNode{ TElemType data;//數據域 struct BiTNode *lchild,*rchild;//左右孩子指針 }BiTNode,*BiTree; void CreateBiTree(BiTree *T){ *T=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->data=1; (*T)->lchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->rchild=NULL; (*T)->lchild->data=2; (*T)->lchild->lchild=(BiTNode*)malloc(sizeof(BiTNode)); (*T)->lchild->rchild=NULL; (*T)->lchild->lchild->data=3; (*T)->lchild->lchild->lchild=NULL; (*T)->lchild->lchild->rchild=NULL; } int main() { BiTree Tree; CreateBiTree(&Tree); printf("%d",Tree->lchild->lchild->data); return 0; }
運行結果:
總結
對於二叉樹和完全二叉樹的性質,需要學員在理解的情況下進行記憶。有關二叉樹存儲結構的選擇,以及結點結構的選擇,要視情況而定,基本上遵循以下兩個原則:
- 如果是普通二叉樹,用鏈式存儲結構;如果是完全二叉樹,用順序存儲結構。
- 如果問題中涉及到要訪問某結點的父結點,就建立三叉鏈表;反之,使用二叉鏈表即可解決問題。