二叉搜索樹以及對二叉搜索樹平衡調整


代碼的思想和圖片參考:好大學慕課浙江大學陳越、何欽銘的《數據結構》

 

我們首先介紹一下什么是二叉搜索樹和二叉平衡樹:

二叉搜索樹:一棵二叉樹,可以為空;如果不為空,滿足以下性質
1. 非空左子樹的所有鍵值小於其根結點的鍵值。
2. 非空右子樹的所有鍵值大於其根結點的鍵值。
3. 左、右子樹都是二叉搜索樹。

二叉搜索樹操作的特別函數:
Position Find( ElementType X, BinTree BST ):從二叉搜索樹BST
中查找元素X,返回其所在結點的地址,查找的次數取決於樹的高度
    算法思想:
    和二分查找類似,如果大於根元素往右邊找,小於根元素網左邊找,可以使用遞歸和非遞歸的方法實現
        注:對尾遞歸,可以用循環來實現。

Position FindMin( BinTree BST ):從二叉搜索樹BST中查找並返回
最小元素所在結點的地址
    算法思想:最小的元素在二叉搜索樹的最左分支的端節點上。一直向左進行遞歸查找即可

Position FindMax( BinTree BST ) :從二叉搜索樹BST中查找並返回
最大元素所在結點的地址
    算法思想:最小的元素在二叉搜索樹的最右分支的端節點上。一直向右進行遞歸查找即可

BinTree Insert( ElementType X, BinTree BST )
    算法思想:和Find的方法差不多,先找到需要插入的位置,然后進行插入
BinTree Delete( ElementType X, BinTree BST )
    算法思想:分為三種情況
        1.刪除的節點是葉子節點:直接刪除
        2.刪除的節點只有一個孩子:刪除該節點,把該節點的唯一的子節點掛到父節點上
        3.該節點是有兩個孩子的父節點:我們可以把兩個孩子的節點看做是有一個孩子的父節點,但是必須從其子節點找到元素來
        替換他,下面是找元素替換的方法。
            3.1查找該節點左子樹的最大元素,把最大元素的值給該節點,然后把那個左子樹最大元素的節點刪除
            3.2查找該節點右子樹的最小元素,把最小元素的值給該節點,然后把那個右子樹最小元素的節點刪除

            為什么需要找最大或者最小元素呢,因為這樣可以最大或者最小元素可以保證該節點只有一個子節點或者沒有節點
            否則找到一個具有兩個節點的父節點,問題還是沒有解決。
        



    
平衡二叉樹:對於二叉搜索樹來說,搜索的次數取決於樹的高度,那么,由於元素的插入順序和規則的不同
    那么所生成樹的形狀和高度也不相同。可以參考講義的十二個月份按照不同規則插入,造成二叉搜索樹的高度差異有很大
    我們希望樹比較平衡,這樣平均查找次數就比較少
    
    定義:
    平衡因子(Balance Factor,簡稱BF):BF(T)=hl-hr,
    hl和hr為左右子樹的高度
    
    平衡二叉樹的定義(Balance Binary tree 或者稱為AVL樹):
        1.空樹
        2.任意節點左右子樹的高度差不超過1,即|BF|<=1

    那么我們需要思考,n個節點的平衡二叉樹的高度為多少呢?能不能滿足我們的需求呢。
    我可以找規律,我們指定只有一個節點時,高度為0,那么就有如下規律
        平衡二叉樹的高度    平衡二叉樹需要的最少節點個數
            0            1        A
            1            2
            2            4
            ...            ...
            
    具體的規律和證明參考講義,最終我們得到一個結論:給定節點為N的AVL樹的最大高度為O(Log2 N)


    平衡二叉樹的調整問題:
    為什么需要調整平衡兒茶樹呢?因為我們隊平衡二叉樹的插入和刪除操作,可能會破壞
    樹的平衡性,所以我們需要對樹的平衡性進行調整。


 

 

平衡二叉樹的調整圖文解析

注:數字代表平衡因子

平衡二叉樹的調整

注:數字代表平衡因子

1 RR調整

調整的演示圖

 

 

上面的不平衡的搜索樹的發現者是Mar,麻煩節點是NovNov在發現者的右子樹的右邊,因而叫做RR插入,需要進行RR旋轉(右單旋)

 

下面給出RR調整的通用的圖解:

 

 

 

 

2 LL調整

 

 LL調整的演示圖:

 

發現者Mar,麻煩節點為Apr在發現者的左子樹的左邊,因而叫LL插入,需要LL旋轉

 

下面是LL調整的通解圖:

 

 

 

3 LR調整

LR調整的演示圖

 

 

發現節點是May,麻煩節點是JanJanMay左子樹的右子樹中,因而叫做LR插入,需要進行LR旋轉

 

LR調整的通解圖:

 

 

 

4 RL調整

RL調整的演示圖:

 

發現節點是Aug,麻煩節點是FebFebAug的右子樹的左子樹上面,因而叫做RL插入,需要進行RL旋轉

 

RL調整的通解圖:

 

 

 

 

 

 

下面是二叉搜索樹的相關操作的代碼:

  1 #include<stdio.h>
  2 #include<stdlib.h>
  3 
  4 typedef int elementType;
  5 
  6 /*define a binary tree*/
  7 typedef struct node{
  8     elementType element;
  9     struct node *left;
 10     struct node *right;
 11     int treeHeight;/*為了平衡二叉樹計算方便,定義樹高*/
 12 }tre,*pTree;
 13 
 14 /*二叉搜索樹的遞歸查找*/
 15 pTree find(elementType element,pTree tree){
 16     if(!tree){
 17         return NULL;
 18     }
 19     if(element<tree->element){
 20         /*查找元素比根元素小在左子樹中查找*/
 21         return  find(element,tree->left);
 22     }else if(element>tree->element){
 23         /*查找元素比根元素小在右子樹中查找*/
 24         return find(element,tree->right);
 25     }else{/*找到該節點,返回*/
 26         return tree;
 27     }
 28 }
 29 
 30 /*因為遞歸方法執行的效率低,而且上面的尾遞歸函數可以改寫為遞歸函數*/
 31 pTree find2(elementType element,pTree tree){
 32     pTree temp;
 33     temp = tree;
 34     while(temp){
 35         if(element<temp->element){
 36             temp=temp->left;
 37         }else if(element>temp->element){
 38             temp=temp->right;
 39         }else{
 40             return temp;
 41         }
 42     }
 43     return NULL;
 44 }
 45 
 46 /*根據二叉搜索樹的特點,最小元素在最左邊的節點上面*/
 47 pTree getMinElement(pTree tree){
 48     if(tree){
 49          while(tree->left){
 50             tree=tree->left;
 51          }
 52     }
 53 
 54     return tree;
 55 }
 56 
 57 /*獲取二叉搜索樹中的最大元素,最大元素的位置應該在最右邊的節點上*/
 58 pTree getMaxElement(pTree tree){
 59     if(tree){
 60         while(tree->right){
 61             tree = tree->right;
 62         }
 63     }
 64 
 65     return tree;
 66 }
 67 
 68 /*二叉搜索樹的插入,知識遵循二叉搜索樹的性質,但是並沒有調節平衡*/
 69 pTree insert(pTree tree,elementType element){
 70     pTree temp;
 71     if(!tree){
 72         tree = (pTree)malloc(sizeof(tre));
 73         tree->element=element;
 74         tree->left=tree->right=NULL;
 75     }else{/**/
 76         if(element<tree->element){
 77             tree ->left = insert(tree->left,element);
 78         }
 79         else if(element>tree->element){
 80             tree->right = insert(tree->right,element);
 81         }
 82     }
 83     return tree;
 84 }
 85 
 86 /*非遞歸的二叉搜索樹的算法*/
 87 pTree insert2(pTree tree,elementType element){
 88     pTree temp;
 89     int flag;
 90     pTree parent;
 91     if(!tree){
 92         tree = (pTree)malloc(sizeof(tre));
 93         tree->element=element;
 94         tree->left=tree->right=NULL;
 95     }else{
 96         temp=tree;
 97         while(temp!=NULL){
 98             if(element<temp->element){
 99                 //printf("lala\n");
100                 parent = temp;
101                 temp=temp->left;
102                 flag=0;
103             }else if(element>temp->element){
104                 parent = temp;
105                 flag=1;
106                 temp=temp->right;
107             }
108         }
109 
110         temp = (pTree)malloc(sizeof(tre));
111         temp->element=element;
112         temp->left=temp->right=NULL;
113         if(flag){
114             parent->right=temp;
115         }else{
116             parent->left=temp;
117         }
118     }
119 
120     return tree;
121 }
122 
123 /*在二叉搜索樹中刪除一個元素
124     算法思想:
125         1.首先查找到該元素
126         2.如果該元素是葉子節點,直接刪除
127         3.如果該元素有一個孩子節點,直接把孩子節點掛載到該節點的父節點上
128         4.如果該節點有兩個孩子,由兩種方法
129             a.在該節點的左子樹中找到最大元素節點T,把該節點的值替換成T的值,然后執行對T的刪除操作
130             b.在該節點的右子樹中找最小元素的節點T,把該節點的值替換為T的值,然后執行對T的刪除操作
131         注:找最大或者最小元素是因為最大最小元素是葉子節點或者只有一個孩子。
132 */
133 pTree deleteElement(pTree tree,elementType element){
134     pTree temp;
135     if(!tree){
136         printf("the element don't search in this tree\n");
137     }else if(element<tree->element){
138         tree->left=deleteElement(tree->left,element);
139     }else if(element>tree->element){
140         tree->right = deleteElement(tree->right,element);
141     }else{//找到需要刪除的元素節點
142         if(tree->left && tree->right){//該有兩個孩子節點
143             temp = getMinElement(tree->right);/*獲取右子樹的最小值節點*/
144             tree->element=temp->element;
145             tree->right=deleteElement(tree->right,temp->element);
146         }else{
147             temp=tree;
148             if(!tree->left){
149                 tree=tree->right;
150             }else if(!tree->right){
151                 tree=tree->left;
152             }
153             free(temp);
154         }
155     }
156     return tree;
157 }
158 
159 /*使用非遞歸的方法
160 pTree deleteElement2(pTree tree,elementType element){
161     pTree temp,maxSubNode,flag,temp2;
162     if(!tree){
163         printf("the tree is empty,don't allow delete elememt\n");
164     }else{
165        temp = find(element,tree);
166        if(temp==NULL){
167             printf("the element don't exsit in this tree\n");
168        }else{
169             if(temp->left && temp->right){
170                 maxSubNode = getMinElement(temp->right);
171                 temp->element = maxSubNode->element;
172             }else{
173                 maxSubNode = temp;
174             }
175 
176                 temp2=maxSubNode;
177                 if(!maxSubNode->left){
178                     maxSubNode=maxSubNode->right;
179                 }else if(!maxSubNode->right){
180                     maxSubNode=maxSubNode->left;
181                 }
182                 free(temp2);
183        }
184 
185     }
186     return tree;
187 }*/
188 
189 
190 //先序遍歷
191 void preOrderTraversal(pTree tree){
192     if(tree){
193         printf("%d ",tree->element);
194         preOrderTraversal(tree->left);
195         preOrderTraversal(tree->right);
196     }
197 }
198 
199 /*=====================================調整樹為平衡二叉樹===============================================*/
200 
201 int getMaxValue(int a,int b){
202     return a > b ? a : b ;
203 }
204 
205 /*獲取二叉樹的樹高*/
206 int getHeight(pTree tree){
207     int leftHeight,rightHeight;
208     if(tree){
209         leftHeight = getHeight(tree->left);
210         rightHeight = getHeight(tree->right);
211         return (leftHeight>rightHeight ? leftHeight : rightHeight)+1;
212     }
213     return 0;
214 }
215 /*
216 左單旋操作:將A與B進行LL旋轉,並更新A和B的新高度,返回新的根節點B
217 A必須有一個左子節點B
218 */
219 pTree singleLeftRatation(pTree A){
220     pTree B = A->left;
221     A->left=B->right;
222     B->right = A;
223     A->treeHeight = getMaxValue(getHeight(A->left),getHeight(A->right))+1;
224     B->treeHeight = getMaxValue(B->left,A->treeHeight)+1;
225     return B;
226 }
227 
228 /*右單旋:將A與B進行RR旋轉,並更新A與B的高度,返回新的根節點B
229 注:A必須有一個右節點B
230 */
231 pTree singleRightRatation(pTree A){
232     pTree B = A->right;
233     A->right = B->left;
234     B->left = A;
235     A->treeHeight = getMaxValue(getHeight(A->left),getHeight(A->right))+1;
236     B->treeHeight = getMaxValue(getHeight(B->right),A->treeHeight);
237     return B;
238 }
239 
240 /*
241 將A做LR旋轉,返回新的根節點C
242 A必須有一個左自己的B,B必須有一個右子節點C
243 */
244 pTree doubleLeftRightRatation(pTree A){
245     /*先對B,C進行RR旋轉,C被返回*/
246     A->left = singleRightRatation(A->left);
247     /*在對A和C進行LL旋轉,返回新的根節點C*/
248     return singleLeftRatation(A);
249 }
250 
251 /*
252 對A進行RL旋轉,返回新的根節點C
253 注:A必須有一個右子節點B,B必須有一個左子節點C
254 */
255 pTree doubleRightLeftRatation(pTree A){
256     /*先對B,C進行LL旋轉,返回新的根節點C*/
257     A->right = singleLeftRatation(A->right);
258     /*在對A,C進行RR旋轉,返回新的根節點C*/
259     return singleRightRatation(A);
260 }
261 
262 /*對二叉搜索樹進行插入,插入后調整樹的平衡*/
263 pTree AVLInsert(pTree tree,elementType element){
264     if(!tree){
265         tree = (pTree)malloc(sizeof(tre));
266         tree->element = element;
267         tree->left=tree->right = NULL;
268         tree->treeHeight=0;
269     }else if(element<tree->element){
270         tree->left = AVLInsert(tree->left,element);
271         //判斷平衡因子是否等於2
272         if(getHeight(tree->left)-getHeight(tree->right) == 2){
273             if(element<tree->left->element){//element往tree的左子樹的左子樹插入導致平衡因子大於2,進行LL調整的
274                 tree = singleLeftRatation(tree);
275             }else{//element往tree的左子樹的右子樹插入導致平衡因子大於2,進行LR調整
276                 tree = doubleLeftRightRatation(tree);
277             }
278         }
279     }else if(element>tree->element){
280         tree->right = AVLInsert(tree->right,element);
281         //判斷平衡因子是否等於2
282         if(getHeight(tree->right)-getHeight(tree->left) == 2){
283             if(element>tree->right->element){//element往tree的右子樹的右子樹插入導致平衡因子大於2,進行RR調整
284                 tree = singleRightRatation(tree);
285             }else{//element往tree的右子樹的左子樹插入導致平衡因子大於2,進行RL調整
286                 tree = doubleRightLeftRatation(tree);
287             }
288         }
289     }/* else 如果找到了,就不進行插入*/
290 
291     tree->treeHeight = getMaxValue(getHeight(tree->left),(getHeight(tree->right)))+1;
292     return tree;
293 }
294 
295 
296 void main(){
297     printf("\n==========普通插入=====================================\n");
298     int findElement=33;
299     int deleteData=41;
300     pTree tree=insert(NULL,30);
301     tree=insert(tree,15);
302     tree=insert(tree,41);
303     tree=insert(tree,33);
304     tree=insert(tree,50);
305     tree=insert(tree,35);
306     preOrderTraversal(tree);
307     printf("\n");
308     printf("The find element is:%d,the result is %d \n",findElement,find(findElement,tree)->element);
309     printf("The min element:%d\n",getMinElement(tree)->element);
310     printf("The max element:%d\n",getMaxElement(tree)->element);
311     //printf("delete the elemet %d\n",deleteData);
312     //deleteElement(tree,deleteData);
313     printf("\nordinary tree preOrder\n");
314     preOrderTraversal(tree);
315 
316     printf("\n==========AVL插入=====================================\n");
317 
318     pTree AVLTree=AVLInsert(NULL,30);
319     AVLTree=AVLInsert(AVLTree,15);
320     AVLTree=AVLInsert(AVLTree,41);
321     AVLTree=AVLInsert(AVLTree,33);
322     AVLTree=AVLInsert(AVLTree,50);
323     AVLTree=AVLInsert(AVLTree,35);
324     printf("\n");
325     printf("The find element is:%d,the result is %d \n",findElement,find(findElement,AVLTree)->element);
326     printf("The min element:%d\n",getMinElement(AVLTree)->element);
327     printf("The max element:%d\n",getMaxElement(AVLTree)->element);
328     //printf("delete the elemet %d\n",deleteData);
329     //deleteElement(AVLTree,deleteData);
330      printf("\nAVL tree preOrder\n");
331     preOrderTraversal(AVLTree);
332 
333 }
二叉搜索樹和平衡二叉樹代碼

 

依次插入 30 15 41 33 50 35

 

這個和程序運行的結果是一致的:

 


免責聲明!

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



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