樹的常用術語的相關定義
樹:樹是n(n>=0)個節點的有限集。
度:結點擁有的子樹稱爲結點的度。度爲0 的結點稱爲葉子結點或終端結點。不爲0的結點稱爲非終端結點或分枝結點。初了根結點之外,分支結點也稱爲內部結點。(如圖1,結點1的度是3,2的度是2。)
樹的度:樹的度是樹內各結點的度的最大值。(如圖1,樹的度是3。)
結點之間關係的定義:結點的子樹的根稱爲該結點的孩子,相應地,該結點稱爲孩子的雙親。同一個雙親的孩子之間互稱爲兄弟。結點的祖先是從根到該結點所經分枝上 的所有結點。反之,以某結點爲根的子樹中的任一結點都稱爲該結點的子孫。
結點的層次:從根開始,根爲第一層,根的孩子爲第二層,以此類推… 。雙親在同一層的結點互爲堂兄弟。
樹的深度:樹中結點的最大層次稱爲樹的高度或深度。(如圖1 樹的深度爲3)
有序樹和無序樹:如果將樹中結點的各子樹看成是從左至右是又次序的,則稱該樹爲有序樹,否則爲無序樹。(如圖1 爲無序樹 圖2爲有序樹)
二叉樹
定義: 二叉樹是另一種樹型結構, 它的特點是每個結點至多隻有兩顆子樹(即二叉樹中不存在度大於2的結點),並且二叉樹的子樹有左右之分,其次序不能任意顛倒。(如圖1 不是二叉樹 圖2 是二叉樹)
性質:
性質 1. 在二叉樹的地i層最多有 2 ^(i-1) 個結點 i>=1。
性質 2. 深度爲k的二叉樹至多有2^k - 1個結點 k>=1。
性質 3. 對任何一顆二叉樹T,如果其終端結點數爲n0,度爲2的結點數爲n2,那麼n0=n2+1。
完全二叉樹和滿二叉樹是兩種特殊形態的二叉樹。
滿二叉樹定義:一顆深度爲k且有2^k - 1個節點的二叉樹稱爲滿二叉樹。(如圖3爲滿二叉樹)
完全二叉樹定義:深度爲k的,有n個結點的二叉樹,當且僅當其中每一個結點都與深度爲k的滿二叉樹中編號從1至n的結點一一對應時,稱之爲完全二叉樹。 (如圖4爲完全二叉樹)
性質 4. 具有n個結點的完全二叉樹的深度爲log2n + 1
性質 5. 如果一顆有n個結點的完全二叉樹的結點按層序編號,對任意結點有:
5.1. 如果i = 1,則結點i是二叉樹的根,無雙親;如果i>1 ,則雙親是i/2。
5.2. 如果2i > n, 則結點i無左孩子;否則其左孩子的結點是2i。
5.3. 如果2i + 1 > n, 則結點i無右孩子,否則其右孩子的結點是2i + 1。
遍歷二叉樹
二叉樹遍歷分爲三種:先序遍歷、中序遍歷、後序遍歷 (時間複雜度爲O(n)空間複雜度O(n))
先序遍歷操作:(1)訪問根結點;(2)先序遍歷左子樹;(3)先序遍歷右子樹; (圖4爲例遍歷後123456)
中序遍歷操作:(1)先序遍歷左子樹;(2)訪問根結點;(3)先序遍歷右子樹; (圖4爲例遍歷後324165)
後序遍歷操作:(1)先序遍歷左子樹;(2)先序遍歷右子樹;(3)訪問根結點; (圖4爲例遍歷後342651)
線索二叉樹
定義:若結點有左子樹,則左指針指向左孩子,否則指向前驅。若結點有右子樹,則左指針指向右孩子,否則指向後繼。以這種結點結構成的二叉鏈表作爲二叉樹的存儲結構,叫做線索鏈表,其中指向結點前驅和後繼的指針,叫做線索。加上線索的二叉樹稱之爲線索二叉樹。對二叉樹以某種次序遍歷使其變爲線索二叉樹的過程叫做線索化。(如圖五 後續後繼線索二叉樹,紅色爲線索)
赫夫曼樹
假設有n個權值 {w1, w2, w3, …, wn},試構造一顆有n個葉子結點的二叉樹,每個葉子結點帶權爲wi, 則其中帶權路徑長度WPL最小的二叉樹稱爲最優二叉樹或赫夫曼樹。
如圖6 的3顆二叉樹,都有4個葉子結點abcd,帶權分別爲7、 5、 2、 4。他們的帶權路徑長度分別爲。
(1)WPL = 7*2 + 5*2 + 2*2 + 4*2 = 36
(2)WPL = 。。。。 = 46
(3)WPL = 。。。。 = 35
如何構建赫夫曼樹?根據給定的權值取最小的兩個作爲葉子結點,把這兩個葉子結點的根結點作爲新的結點權值爲兩個葉子結點權值的和,以此類推。。。
二叉排序樹(又稱二叉查找樹)
性質:(1) 若左子樹不空,則左子樹上所有結點的值均小於它的根結點的值;(2)若右子樹不空,則右子樹上所有結點的值均大於它的根結點的值; (3)左、右子樹也分別爲二叉排序樹;
二叉排序樹的查找:若根結點的關鍵字值等於查找的關鍵字,成功。否則如果小於關鍵字,查左子樹,大於關鍵字查右子樹。
平均查找長度計算方式:時間複雜度 log(n) (證明待寫)
排序二叉樹的插入:
struct BiTree {
int data;
BiTree *lchild;
BiTree *rchild;
};
//在二叉排序樹中插入查找關鍵字key
BiTree* InsertBST(BiTree *t,int key)
{
if (t == NULL)
{
t = new BiTree();
t->lchild = t->rchild = NULL;
t->data = key;
return t;
}
if (key < t->data)
t->lchild = InsertBST(t->lchild, key);
else
t->rchild = InsertBST(t->rchild, key);
return t;
}
//n個數據在數組d中,tree爲二叉排序樹根
BiTree* CreateBiTree(BiTree *tree, int d[], int n)
{
for (int i = 0; i < n; i++)
tree = InsertBST(tree, d[i]);
}
排序二叉樹的刪除
排序二叉樹的刪除比較複雜 有四種情況:
1. 節點無子節點時 直接刪除 (如圖(a))
2. 節點只有左子樹時 直接用左孩子節點替換被刪除節點 (如圖(b))
3. 節點只有右子樹時 直接用右孩子節點替換被刪除節點 (類似圖(b))
4. 節點左右子樹都存在時 我們可以用被刪除的節點的前驅 或者後繼 代替該結點 (如圖(c))
4.1 當用前驅代替時,若被刪除節點的左子節點無右子樹,直接用左子節點替換被刪除節點,將被刪除節點的左指針指向子左節點的左子樹(如圖c1)。若被刪除節點左子節點有右子樹,循環查找到被刪節點的前驅,也就是左子樹的最右下方的節點,替換爲被刪節點,將最右下方的節點的父節點的右指針指向最右下方節點左孩子(如圖c2)。( 如下代碼是按照前驅代替被刪節點實現的)
4.2 當用後繼代替時。。。 大家自己去想一下。
代碼如下:
bool deleteTree(BTree **b,int key){
if(!*b)
return false;
else{
if((*b)->data == key){
return deleteNode(&(*b));
}
else if((*b)->data > key)
return deleteTree(&(*b)->lchild,key);
else
return deleteTree(&(*b)->rchild,key);
}
}
bool deleteNode(BTree **b){
BTree *p,*s;
if((*b)->lchild == NULL ){
p = (*b);
(*b) = (*b)->rchild;
free(p);
}else if((*b)->rchild == NULL){
p = (*b);
(*b) = (*b)->lchild;
free(p);
}else{
p = (*b);
s = (*b)->lchild;
while(s->rchild != NULL){
p = s;
s = s->rchild;
}
(*b)->data = s->data;
if (p != *b)
p->rchild = s->lchild; // 用b的前驅替換b
else
p->lchild = s->lchild;
free(s);
return true;
}
}
參考教材:《 數據結構 》清華大學出版社 嚴蔚敏 吳偉民著