二叉排序樹和平衡樹


B樹的結構有:B-Tree, B-Tree, B*Tree

BTree(二叉排序樹)B-Tree:B樹也是二叉排序樹的變異版本,是N叉的排序樹。

M階BTree的幾個重要特性

1.結點最多含m棵子樹(指針),m-1個關鍵字(存的數據,空間)(m >= 2)

2.除根節點和葉子結點外,其它每個結點至少有ceil(m / 2)個子節點,ceil向上取整

3.若根節點不是葉子節點,則至少有兩棵子樹。

 

二叉排序樹BST,也稱二叉查找樹

在二叉查找樹中,因為中序遍歷的順序是 左子樹, 根, 右子樹,而在二叉排序樹中,左子樹結點值 < 根節點值 < 右子樹結點值,所以,二叉排序樹的中序遍歷序列是遞增的結果。

二叉排序樹的中序遍歷序列一定是一個遞增的有序序列。

 

對於這棵樹,中序遍歷序列是:1, 2, 3, 4, 5, 7, 8, 10, 16

對於二叉排序樹而言,我們最常用的操作並不是排序,而是查找。

查找操作

1.判斷二叉樹是否為空

2.二叉樹若不為空,則查找根節點,若相等則查找成功

3.若根節點不相等,則當小於根節點則查找左子樹;當大於根結點值時則查找右子樹。

4.當查找到葉節點仍沒查找到相對應的值(判斷指針是否為空),則查找失敗。

該算法的時間復雜度為O(h)

 1 BSTNode* BST_Search(BiTree T,  ElemType key, BSTNode* &p)
 2 {
 3   p = NULL;
 4     while(T != NULL && key != T.data)
 5     {
 6        if(key < T.data)
 7          T = T -> lchild;
 8        if(key > T.data)
 9          T = T -> rchild;
10     }  
11    return T;
12 
13 }

 

插入

若二叉排序樹為空,則直接插入結點

若二叉排序樹非空,當值小於根節點時插入左子樹;當值大於根節點時,插入右子樹,當值等於根節點時不進行插入。

注意,在樹的實現中,我們是通過二叉鏈表或者三叉鏈表來實現的,所以我們在插入操作或者刪除操作時修改指針指向即可。

 1 //返回的是整型變量,若插入成功則返回值1,插入失敗則返回值0
 2 int BST_Insert(BiTree &T, KeyType k){
 3     if(T == NULL)
 4    {
 5     T  = (BiTree)malloc(sizeof(BSTNode));
 6     T->data = k;
 7     T -> lchild = NULL;
 8     T -> rchild = NULL;
 9     return1;
10    }
11 
12     else if(key == T->data)
13     return 0;
14 
15     else if(key > T -> data)
16         return BST_Insert(T->rchild, k);
17   
18     else
19         return BST_Insert(T->lchild, k)
20     
21 }

構造二叉排序樹

構造二叉排序樹是一個動態的問題。就是不斷調用插入函數進行構造二叉排序樹。

讀入一個元素並建立結點,若二叉樹為空,則將其作為根節點;

若二叉樹非空,當值小於根結點時,插入左子樹;當值大於根節點時,插入右子樹;當值等於根節點時不進行插入。

1 void Create_BST(BiTree &T, KeyType str[], int n){
2     T = NULL;
3     int i = 0; 
4     while(i < n)
5    {
6       BST_Insert(T, str[i]);
7      i++;
8     }
9 }

但是這樣的構造法生成的二叉順序樹與元素的順序有關

 

刪除操作

二叉排序樹的刪除操作是比較復雜的,分為以下三種情況

1.所被刪除的結點是葉節點,則直接刪除

2.若被刪除的結點只有一棵子樹,則讓該結點的子樹稱為該節點父結點的子樹,從而代替該節點

3.若被刪除的結點右兩棵子樹,則讓該節點的中序序列直接后繼代替該節點,並刪除直接后繼結點。

這里只做第三種情況的分析。

第三種情況中,被刪除的中序直接后繼節點是比該結點值大,但是又是比被刪除結點的右子樹中其他結點值小的一個結點,所以將被刪除結點的中序直接后繼結點代替該結點時最合適的操作。

 

在刪除二叉排序樹中刪除並插入某結點,得到的二叉排序樹是否與原來相同。

答案是可能相同,也可能不同,要視情況具體分析。

仍以上圖中圖1所示的二叉排序樹為例,若刪除的是結點7,那么得到的二叉排序樹是相同的。

但是若刪除的是結點5,那么再重新將該節點插入該二叉樹時,結點5就會成為結點7的左孩子結點。

二叉排序樹的查找效率

平均查找長度(ASL)取決於樹的高度。

如果二叉排序樹是平衡二叉樹的話,查找效率是O(log2n), 最壞情況下是O(n),此時二叉排序樹類似於單鏈表。

 

平衡二叉樹

平衡二叉樹AVL:任意結點的平衡因子的絕對值不超過1。

平衡因子:左子樹高度與右子樹高度之差

 

高度為h的最小平衡二叉樹的結點數Nh的計算

Nh = Nh-1 + Nh-2 + 1

N0 = 0

N1 = 1

平衡二叉樹的后序遍歷過程

利用遞歸的后序遍歷過程:

1.判斷左子樹是一棵平衡二叉樹

2.判斷右子樹是一棵平衡二叉樹

3.判斷以該節點為根的二叉樹為平衡二叉樹

判斷條件:若左子樹和右子樹均為平衡二叉樹,且左子樹與右子樹高度差的絕對值小於等於1,則平衡。

需要保留兩個變量:表示平衡性的變量B,為布爾類型,平衡為1,不平衡為0;表示高度的變量,為整型。

 

 1 void Judge_AVL(BiTree bt, int &balance, int &h)
 2 {
 3     //左子樹的平衡性、右子樹的平衡性, 左子樹的高度,右子樹的高度
 4    int bl = 0; br = 0, hl = 0; hr = 0;
 5    if(bt == NULL)
 6     {
 7       h = 0;
 8       balance = 1;
 9     }
10 
11     else if(bt -> lchild == NULL && bt -> rchild == NULL)
12     {
13         h = 1;
14         balance = 1;
15     }
16  
17     else
18     {
19          Judge_AVL(bt->lchild, bl, hl);
20          Judge_AVL(bt -> rchild, br, hr);
21          if(hl > hr)
22             h = hr + 1;
23          else
24             h = hl + 1;
25     
26 
27          if(abs(hl - hr) < 2 && bl ==1 && br == 1)
28              balance = 1;
29          else
30              balance = 0;
31     }
32 }

 

插入操作

先按照二叉排序樹的插入過程進行插入,然后對插入后的二叉排序樹進行調整,將其調整為平衡二叉樹。

調整過程:調整過程是針對最小的不平衡子樹,也就是從我們的插入結點開始依次向上找到最小的不平衡子樹。

根據不平衡的情況,我們分為了四種調整的過程。

LL平衡旋轉(右單旋轉)

原因:在結點A的左孩子的左子樹上插入新結點導致二叉樹的不平衡

調整方法:右旋操作:將A的左孩子B代替A,將A結點稱為B的右子樹根節點,而B的原右子樹作為A的左子樹。

 

RR平衡旋轉(左單旋轉)

原因:在結點A的右孩子的右子樹上插入了新結點導致了二叉樹的不平衡

調整:左旋操作,將A的右孩子B代替A,將A結點稱為B的左子樹根結點,而B的原左子數則作為A的右子樹。

 

LR平衡旋轉(先左后右雙旋轉)

原因:在結點A的左孩子的右子樹上插入了新結點

調整:先左旋后右旋操作:先將A的左孩子B的右孩子結點C代替B,然后再將C結點向上代替A的位置

1.在本圖中,既可以插到C結點的左子樹CL上,也可以插到C結點的右子樹CR上

2.本圖是將結點插入到了CL子樹上,所以CL是H層, CR是H-1層。

3.之所以標注(H)是考慮到CL和CR可能是空的

RL平衡旋轉(先右后做雙旋轉)

原因:在結點A的右孩子的左子樹上插入了新結點

調整方法:先右旋后左旋操作:將A的右孩子B的左孩子結點C代替B,然后再將C結點向上代替A的位置

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM