二叉查找樹(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_