二叉查找樹(BST)、平衡二叉樹(AVL樹)


二叉查找樹(BST)

  特殊的二叉樹,又稱為排序二叉樹、二叉搜索樹、二叉排序樹。

  二叉查找樹實際上是數據域有序的二叉樹,即對樹上的每個結點,都滿足其左子樹上所有結點的數據域均小於或等於根結點的數據域,右子樹上所有結點的數據域均大於根結點的數據域。如下圖所示:


二叉查找樹通常包含查找、插入、建樹和刪除操作。

二叉查找樹的創建

對於一棵二叉查找樹,其創建與二叉樹的創建很類似,略有不同的是,二叉查找樹,為了保證整棵樹都關於根結點的大小呈左小右大的特征,在創建時,需要根據當前結點的大小來判斷插入位置,給出如下代碼:

template<typename T>
void  BSTree<T>::createBSTreeByFile(ifstream &f){
    T e;
    queue<BSNode<T>*> q;

    while(!f.eof()){
        InputFromFile(f, e);
        Insert(root, e);
    }
}

template<typename T>
void BSTree<T>::Insert(BSNode<T>* &t, T x){//得用指針的引用,不然傳參時由於形參實例化,並不能成功創建二叉樹

    if(t==NULL){
        t = new BSNode<T>;
        t->data = x;
        t->lchild = t->rchild = NULL;
        return;
    }

    if(x<=t->data){
        Insert(t->lchild, x);
    }
    else{
        Insert(t->rchild, x);
    }
}

 

二叉查找樹的查找

二叉查找樹的查找有遞歸和非遞歸兩種,對於遞歸方式,其遞歸邊界為樹的終止結點,非遞歸方式則采取對樹中所有結點采取BFS或者DFS進行遍歷的方式。

對於非遞歸方式,給出采取DFS的遍歷方式,在這種方式中,通常采用入棧的方式,來訪問每個結點,而根據訪問的先后順序,又分為,前序、中序和后序三種遍歷方式。以前序遍歷為例,通常以根、左、右的順序訪問遍歷每個結點,而中序遍歷方式,則以左、根、右的順序遍歷,后序則以左右根的順序來訪問。下面給出三種遍歷方式的代碼:前序遍歷:

 1 template<typename T>
 2 void BSTree<T>::PreOrderTraverse(void(*visit)(BSNode<T>&))const{
 3     stack<BSNode<T>*> s;
 4     BSNode<T> *t = root;
 5     while(NULL!=t || !s.empty()){
 6         if(NULL!=t){
 7             s.push(t);
 8             visit(*t);
 9             t = t->lchild;
10         }
11         else{
12             t = s.top();
13             s.pop();
14             t = t->rchild;
15         }
16     }
17     cout<<endl;
18 }

 中序遍歷:

 1 template<typename T>
 2 void BSTree<T>::InOrderTraverse(void(*visit)(BSNode<T>&))const{
 3     stack<BSNode<T>*> s;
 4     BSNode<T> *q;
 5 
 6     q = root;
 7 
 8     while(!s.empty()||q!=NULL){
 9         if(q!=NULL){
10             s.push(q);
11             q = q->lchild;
12         }
13         else{
14             q = s.top();
15             s.pop();
16             visit(*q);
17             q = q->rchild;
18         }
19     }
20     cout<<endl;
21 }

后序遍歷,對於后序遍歷,直接采用入棧的方式進行訪問,是不行的,因為根結點被訪問兩次,無法保證你在彈棧后,對該結點如何操作,因此,需要另設置一個flag參數,來指明該節點是否左右子樹都訪問過,代碼如下,我這里是令定義一個結構體,來實現:

/*結構體部分*/
enum Tags{Left, Right};

template<typename T>struct StackElem
{
    BSNode<T> *p;
    Tags flag;
};

/*后序遍歷代碼部分*/
template<typename T>
void BSTree<T>::PostOrderTraverse(void(*visit)(BSNode<T>&))const{
    StackElem<T> se;
    stack<StackElem<T> > s;

    BSNode<T> *t;
    t = root;

    if(t==NULL){
        return;
    }
    while(t!=NULL||!s.empty()){
        while(t!=NULL){
            se.flag = Left;
            se.p = t;
            s.push(se);
            t = t->lchild;
        }
        se = s.top();
        s.pop();
        t = se.p;
        if(se.flag==Left){
            se.flag = Right;
            s.push(se);
            t = t->rchild;
        }
        else{
            visit(*t);
            t = NULL;
        }
    }
}

以下是遞歸實現部分,遞歸實現,則是以二叉樹邊界為遞歸邊界,前面已經說過了,其余邏輯與非遞歸一致,因為遞歸的過程,可以看作是一個入棧和彈棧的過程,即,在未到達邊界時,通過遞歸,來訪問下一個結點,例如左結點,當觸及邊界,則訪問該結點,由於每次遞歸狀態都被計算機保存,因此,在訪問一個結點以后,返回上一個結點的狀態,會依次訪問上去。

 遞歸前序遍歷:

 1 template<typename T>
 2 void BSTree<T>::PreTraverse(BSNode<T> *t, void(*visit)(BSNode<T>&))const{
 3     if(t==NULL){
 4         return;
 5     }
 6     else{
 7         visit(*t);
 8         PreTraverse(t->lchild, visit);
 9         PreTraverse(t->rchild, visit);
10     }
11 }

遞歸中序遍歷:

 1 template<typename T>
 2 void BSTree<T>::InTraverse(BSNode<T> *t, void(*visit)(BSNode<T>&))const{
 3     if(t==NULL){
 4         return;
 5     }
 6     else{
 7         InTraverse(t->lchild, visit);
 8         visit(*t);
 9         InTraverse(t->rchild, visit);
10     }
11 }

遞歸后序遍歷:

1 template<typename T>
2 void BSTree<T>::PostTraverse(BSNode<T> *t, void(*visit)(BSNode<T>&))const{
3     if(t!=NULL){
4         PostTraverse(t->lchild, visit);
5         PostTraverse(t->rchild, visit);
6         visit(*t);
7     }
8 }

 平衡二叉樹(AVL樹)

  平衡二叉樹是由前蘇聯的兩位數學家G.M.Adelse-Velskil和E.M.Landis提出,因此一般也稱作AVL樹,AVL樹本質還是一棵二叉查找樹,只是在其基礎上增加了“平衡”的要求。所謂平衡是指,對AVL樹的任意結點來說,其左子樹與右子樹的高度之差的絕對值不超過1,其中左子樹與右子樹的高度因子之差稱為平衡因子。

  如下所示,就是一棵由{1,2,3,4,5,7,8}構建的AVL樹:

  

  只要能隨時保證每個結點平衡因子的絕對值不超過1,AVL的高度就始終能保持O(logn)級別,由於需要對每個結點都得到平衡因子,因此需要在樹的結構中加入一個變量height來記錄以當前結點為根結點的子樹的高度。

AVL樹的創建

  AVL樹的創建是基於二叉查找樹的插入代碼的基礎上,增加平衡操作的。需要從插入的結點開始從下往上判斷結點是否失衡,因此,需要在調用insert函數以后,更新當前子樹的高度,並在這之后根據樹型來進行相應的平衡操作。那么,怎么進行平衡操作呢?AVL樹的插入是需要采取左旋或者右旋操作的,即,插入后,由於插入操作,導致某棵子樹的高度超過了另一棵子樹高度的2個結點高度,這樣就破壞了樹的平衡性,需要做出調整。

右旋操作

如下所示一棵簡單的AVL樹,對其進行插入操作以后:

一棵簡單的AVL樹

變成了下圖這樣的AVL樹:

這樣子就失衡了,所謂右旋操作,就是將這棵AVL樹,從最靠近插入結點的失衡結點處,通過往右子樹調整,使整棵樹的每個結點的平衡因子變為正常,不如上圖的樹,離插入節點3最近的失衡結點是7,


則可以通過下圖所示的操作,來平衡二叉樹,即調整整棵樹平衡因子:

同樣,左旋也與此類似。但是,如果5結點本身就有右結點,即如下所示:

這樣,在經過右旋操作以后,這棵樹還是不平衡的,旋轉后這棵樹如下所示:

因此,還需要進行一次旋轉,顯然,繼續右旋已經無法滿足我們的需求,那么要如何進行操作,才能使這棵樹回復平衡呢?(在后續中,會進行講解)

左旋操作

 左旋操作與右旋操作是類似的,都屬於對子樹的單旋轉。

左旋與右旋一樣,同樣也存在這樣的問題,如果該樹的右子樹的左結點存在,則單一通過左旋是做不到的,那么應該如何處理呢?

 其實,以L和R來表示,插入結點的位置,有以下四種情況:

 

 從上表可以看出,左旋和右旋兩種情況中,左右結點若存在的話,就是上表中的RL和LR情況。則,只需要對兩種情況分別按照上表采取相應的操作就可以解決,如下圖所示:

LR型

RL型

 

 由此,就能實現AVL樹的平衡,下面給出代碼:

AVLTree.h

  1 #ifndef _AVLTREE_H_
  2 #define _AVLTREE_H_
  3 #include "C.h"
  4 #include "AVLNode.h"
  5 #include "Function.h"
  6 
  7 typedef int T;
  8 
  9 using namespace std;
 10 
 11 template<typename T>
 12 class AVLTree{
 13 private:
 14     AVLNode<T> *root;
 15     Destroy(AVLNode<T> *t){
 16         if(t!=NULL){
 17             Destroy(t->lchild);
 18             Destroy(t->rchild);
 19             delete t;
 20             t = NULL;
 21         }
 22         return 0;
 23     }
 24 public:
 25     AVLTree(){
 26         root = NULL;
 27     }
 28     ~AVLTree(){
 29         Destroy(root);
 30     }
 31 
 32     AVLNode<T>* newAVLNode(T x);    //創建新結點
 33     void Insert(AVLNode<T>* &t, T x);
 34     void createAVLTreeFromFile(ifstream &f);
 35     AVLNode<T>* Root()const;
 36     int AVLTreeDepth(AVLNode<T> *t)const;
 37     int getAVLTreeHeight(AVLNode<T>* t)const;   //獲取當前結點的高度
 38     int getBalanceFactor(AVLNode<T>* t)const;   //計算當前結點的高度
 39     void updateAVLNodeHeight(AVLNode<T>* &t);
 40     T getElem(AVLNode<T>* t)const;
 41     bool getElemExist(AVLNode<T>* &t)const;
 42     void LeftRotation(AVLNode<T>* &t);
 43     void RightRotation(AVLNode<T>* &t);
 44     void PreOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const;
 45     void PostOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const;
 46 };
 47 
 48 template<typename T>
 49 AVLNode<T>* AVLTree<T>::newAVLNode(T x){
 50     AVLNode<T>* avlnode = new AVLNode<T>;
 51     avlnode->data = x;
 52     avlnode->height = 1;
 53     avlnode->lchild = avlnode->rchild = NULL;
 54     return avlnode;
 55 }
 56 
 57 template<typename T>
 58 void AVLTree<T>::Insert(AVLNode<T>* &t, T x){
 59     if(t==NULL){
 60         t = newAVLNode(x);
 61         return;
 62     }
 63     if(x==t->data){//結點已經存在,直接返回
 64         return;
 65     }
 66     if(x < t->data){
 67         Insert(t->lchild, x);
 68         updateAVLNodeHeight(t);
 69         if(getBalanceFactor(t)==2){
 70             if(getBalanceFactor(t->lchild)==1){
 71                 RightRotation(t);
 72             }
 73             else if(getBalanceFactor(t->lchild)==-1){
 74                 LeftRotation(t->lchild);
 75                 RightRotation(t);
 76             }
 77         }
 78     }
 79     else{
 80         Insert(t->rchild, x);   //值比當前結點大,往右子樹插入
 81         updateAVLNodeHeight(t);     //更新樹高
 82         if(getBalanceFactor(t)==-2){
 83             if(getBalanceFactor(t->rchild)==-1){ //RR型
 84                 LeftRotation(t);
 85             }
 86             else if(getBalanceFactor(t->rchild)==1){
 87                 RightRotation(t->rchild);
 88                 LeftRotation(t);
 89             }
 90         }
 91     }
 92 }
 93 
 94 template<typename T>
 95 void AVLTree<T>::createAVLTreeFromFile(ifstream &f){
 96     T e;
 97     while(!f.eof()){
 98         InputFromFile(f, e);
 99         Insert(root, e);
100     }
101 }
102 
103 template<typename T>
104 AVLNode<T>* AVLTree<T>::Root()const{
105     return root;
106 }
107 
108 template<typename T>
109 int AVLTree<T>::AVLTreeDepth(AVLNode<T> *t)const{
110     int i,j;
111     if(t==NULL){
112         return 0;
113     }
114     else{
115         i = AVLTreeDepth(t->lchild);
116         j = AVLTreeDepth(t->rchild);
117     }
118     return i>j ? i+1 : j+1;
119 }
120 
121 template<typename T>
122 int AVLTree<T>::getAVLTreeHeight(AVLNode<T>* t)const{
123     if(t==NULL){
124         return 0;
125     }
126     return t->height;
127 }
128 
129 template<typename T>
130 int AVLTree<T>::getBalanceFactor(AVLNode<T>* t)const{
131     if(t==NULL){
132         return 0;
133     }
134     return getAVLTreeHeight(t->lchild) - getAVLTreeHeight(t->rchild);
135 }
136 
137 template<typename T>
138 void AVLTree<T>::updateAVLNodeHeight(AVLNode<T>* &t){
139     t->height = max(getAVLTreeHeight(t->lchild), getAVLTreeHeight(t->rchild)) + 1;
140 }
141 
142 template<typename T>
143 T AVLTree<T>::getElem(AVLNode<T>* t)const{
144     return t->data;
145 }
146 
147 template<typename T>
148 bool AVLTree<T>::getElemExist(AVLNode<T>* &t)const{//判斷當前結點是否為空
149     if(t!=NULL){
150         return true;
151     }
152     return false;
153 }
154 
155 template<typename T>
156 void AVLTree<T>::LeftRotation(AVLNode<T>* &t){
157     AVLNode<T> *temp = t->rchild;
158     t->rchild = temp->lchild;
159     temp->lchild = t;
160     updateAVLNodeHeight(t);
161     updateAVLNodeHeight(temp);
162     t = temp;
163 }
164 
165 template<typename T>
166 void AVLTree<T>::RightRotation(AVLNode<T>* &t){
167     AVLNode<T> *temp = t->lchild;
168     t->lchild = temp->rchild;
169     temp->rchild = t;
170     updateAVLNodeHeight(t);
171     updateAVLNodeHeight(temp);
172     t = temp;
173 }
174 
175 template<typename T>
176 void AVLTree<T>::PreOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const{
177     if(t!=NULL){
178         visit(*t);
179         PreOrderTraverse(t->lchild, visit);
180         PreOrderTraverse(t->rchild, visit);
181     }
182 }
183 
184 template<typename T>
185 void AVLTree<T>::PostOrderTraverse(AVLNode<T>* t, void(*visit)(AVLNode<T>&))const{
186     if(t!=NULL){
187         PostOrderTraverse(t->lchild, visit);
188         PostOrderTraverse(t->rchild, visit);
189         visit(*t);
190     }
191 }
192 #endif // _AVLTREE_H_

Function.h

 1 #ifndef _FUNCTION_H_
 2 #define _FUNCTION_H_
 3 #include "C.h"
 4 #include "AVLNode.h"
 5 #include "AVLTree.h"
 6 
 7 typedef int T;
 8 
 9 using namespace std;
10 
11 bool InputFromFile(ifstream &f, T &e){
12     f>>e;
13     return f.good();
14 }
15 
16 void visit(AVLNode<T> &t){
17     cout<<t.data<<" ";
18 }
19 
20 #endif // _FUNCTION_H_

C.h

 1 #ifndef _C_H_
 2 #define _C_H_
 3 #include<iostream>
 4 #include<string>
 5 #include<stdio.h>
 6 #include<algorithm>
 7 #include<map>
 8 #include<math.h>
 9 #include<queue>
10 #include<stack>
11 #include<vector>
12 #include<fstream>
13 #include<assert.h>
14 #endif // _C_H_

AVLNode.h

 1 #ifndef _AVLNODE_H_
 2 #define _AVLNODE_H_
 3 
 4 typedef int T;
 5 
 6 template<typename T>
 7 struct AVLNode{
 8     int height;     //平衡因子
 9     T data;     //數據域
10     AVLNode<T> *lchild, *rchild;    //指針域
11 };
12 #endif // _AVLNODE_H_

 


免責聲明!

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



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