二叉搜索樹的概念
從前面討論折半搜索的性能中可知,如果每次從搜索序列的中間進行搜索,把區間縮小一半,通過有限次迭代,很快就能通近到所要尋找的元素。進一步,如果我們直接輸入搜索序列,構造出類似於折半搜索的判定樹那樣的樹形結構,就能實現快速搜索。這種樹形結構就是二又搜索樹。
二又搜索樹(binary search tree)或者是一棵空樹,或者是具有下列性質的二又樹:
(1)每個結點都有一個作為搜索依據的關鍵碼(key),所有結點的關鍵碼互不相同。
(2)左子樹(如果存在)上所有結點的關鍵碼都小於根結點的關鍵碼。
(3)右子樹(如果存在)上所有結點的關鍵碼都大於根結點的關鍵碼。
(4)左子樹和右子樹也是二又搜索樹。
關鍵碼事實上是結點所保存元素中的某個域的值,它能夠唯一地表示這個結點。因此,如果對一棵二又搜索樹進行中序遍歷,可以按從小到大的順序,將各結點關鍵碼排列起來,所以也稱二叉搜索樹為二又排序樹(binary sorting tree)。
二叉搜索樹的建立
輸入一系列數據,建立一棵二又搜索樹。它要求從空樹開始建樹,輸入序列以輸入一個結束標志value結束。這個值應當取不可能在輸入序列中出現的值,例如輸入序列的值都是正整數時,取RefValue為0或負數。
1 //構造BST 2 BST(T value) :root(NULL), RefValue(value) 3 { 4 T x; 5 cin >> x; 6 while (x != RefValue) 7 { 8 Insert(x, root); //新建一個結點,調用Insert插入到樹中 9 cin >> x; 10 } 11 }
二叉搜索樹的插入
為了向二又搜索樹中插入一個新元素,必須先檢查這個元素是否在樹中已經存在。所以在插入之前,先使用搜索算法在樹中檢查要插入元素有還是沒有。如果搜索成功,說明樹中已經有這個元素,不再插入;如果搜索不成功,說明樹中原來沒有關鍵碼等於給定值的結點,把新元素加到搜索操作停止的地方。當ptr!=NULL時,它一定指向一棵子樹的根,可用它所包含的關鍵碼與給定值比較繼續搜索插入位置;如果 ptr=NULL,一定是遞歸到空樹的位置,此時將創建的新結點地址送給ptr,因為ptr是引用,新結點的地址自然送入上述某一個指針域,自動將新結點作為葉結點鏈入二又搜索樹中了。每次結點的插入,都要從根結點出發搜索插入位置,然后把新結點作為時結點插入。這樣不需移動結點,只需修改某個已有樹中結點的一個空指針即可。
1 //以ptr為根的二叉搜索樹中插入所含值為e1的結點 2 bool Insert(const T& e1, BSTNode<T>* &ptr) //第二個參數是指針的引用 3 { 4 if (ptr == NULL) 5 { 6 ptr = new BSTNode<T>(e1); //構造新結點 7 if (ptr == NULL) 8 { 9 cout << "Memory allocation failed!" << endl; 10 exit(1); 11 } 12 return true; 13 } 14 else if (e1 < ptr->data) //小於,插入左子樹 15 { 16 Insert(e1, ptr->left); 17 } 18 else if (e1 > ptr->data) //大於,插入右子樹 19 { 20 Insert(e1, ptr->right); 21 } 22 else //x已在樹中,不插入 23 { 24 return false; 25 } 26 }
二叉搜索樹的遞歸搜索
從根結點開始,沿某一個分支逐層向下進行比較判等的過程。它可以是一個遞歸的過程。假設想要在二又搜索樹中搜索關鍵碼為x的元素,搜索過程從根結點開始。如果根指針為NULL,則搜索不成功;否則用給定值x與根結點的關鍵碼進行比較:如果給定值等於根結點的關鍵碼,則搜索成功,返回搜索成功信息,並報告搜索到的結點地址。如果給定值小於根結點的關鍵碼,則繼續遞歸搜索根結點的左子樹,否則,遞歸搜索根結點的右子樹。
1 //在ptr為根的二叉搜索樹中搜索含x的結點。若找到,返回該結點地址,否則返回NULL 2 BSTNode<T>* Search(T x, BSTNode<T>* ptr) 3 { 4 if (ptr == NULL) 5 { 6 return NULL; 7 } 8 else if (x < ptr->data) 9 { 10 return Search(x, ptr->left); 11 } 12 else if (x > ptr->data) 13 { 14 return Search(x, ptr->right); 15 } 16 else 17 { 18 return ptr; 19 } 20 }
二叉搜索樹的刪除
在二又搜索樹中刪除一個結點時,必須將因刪除結點而斷開的二又鏈表重新鏈接起來,同時確保二叉搜索樹的性質不會失去。此外,為了保證在執行刪除后,樹的搜索性能不至於降低,還需要防止重新鏈接后樹的高度不能增加。

如果想要刪除葉結點,只需將其父結點指向它的指針清零,再釋放它即可。
如果被刪結點右子樹為空,可以拿它的左子女結點頂替它的位置,再釋放它。
如果被刪結點左子樹為空,可以拿它的右子女結點頂替它的位置,再釋放它。
如果被刪結點左、右子樹都不空,可以在它的右子樹中尋找中序下的第一個結點(關鍵碼最小),用它的值填補到被刪結點中,再來處理這個結點的刪除問題,這是一個遞歸處理。例如,在上圖中想要刪除關鍵碼為78的結點,它的左、右子樹都不空。在它的右子樹中找中序下的第一個結點,其關鍵碼為81。把它的值填補到被刪結點中去,下面的問題就是刪除關鍵碼為81的結點了。這個結點左子樹為空,用它的右子女(關鍵碼為88)代替它的位置就可以了。
1 //以ptr為根的二叉搜索樹中刪除含x的結點 2 bool Remove(T x, BSTNode<T>* &ptr) 3 { 4 BSTNode<T>* temp; 5 if (ptr != NULL) //ptr不為空進行操作 6 { 7 if (x < ptr->data) 8 { 9 Remove(x, ptr->left); 10 } 11 else if (x > ptr->data) 12 { 13 Remove(x, ptr->right); 14 } 15 //找到了要刪除的結點 16 //1.要刪除的結點ptr同時有左右子樹 17 else if (ptr->left != NULL&&ptr->right != NULL) 18 { 19 temp = ptr->right; //在右子樹中搜索中序下的第一個結點 20 while (temp->left != NULL) 21 { 22 temp = temp->left; 23 } 24 //用右子樹中序下的第一個結點的值填充要刪除的結點 25 ptr->data = temp->data; 26 //然后再新填充值ptr的右子樹中刪除temp的data值 27 Remove(ptr->data, ptr->right); 28 } 29 else //不同時有左右子樹 30 { 31 temp = ptr; //temp記住要刪除的ptr結點 32 if (ptr->left == NULL) //只有右子樹 33 { 34 ptr = ptr->right; 35 } 36 else //只有左子樹 37 { 38 ptr = ptr->left; 39 } 40 delete temp; //刪除結點 41 temp = NULL; 42 return true; 43 } 44 } 45 else //ptr為空直接返回false 46 { 47 return false; 48 } 49 }
二叉搜索樹的銷毀
二叉搜索樹的銷毀與普通二叉樹基本相同。
1 //銷毀以root為根的二叉樹搜索樹函數 2 void Destroy(BSTNode<T>* &root) 3 { 4 if (root == NULL) 5 { 6 return; 7 } 8 if (root->left != NULL) 9 { 10 Destroy(root->left); 11 } 12 if (root->right != NULL) 13 { 14 Destroy(root->right); 15 } 16 delete root; 17 root = NULL; 18 }
二叉搜索樹的源碼

1 //二叉搜索樹結點類型 2 template<typename T> 3 struct BSTNode 4 { 5 T data; //數據域 6 BSTNode<T> *left, *right; //左子女、右子女 7 BSTNode() :left(NULL), right(NULL) {} //構造函數 8 //構造函數 9 BSTNode(const T d, BSTNode<T>* L = NULL, BSTNode<T>* R = NULL) :data(d), left(L), right(R) {} 10 }; 11 12 //二叉搜索樹的定義 13 template <class T> 14 class BST 15 { 16 public: 17 //普通構造函數 18 BST() :root(NULL) {} 19 //構造BST 20 BST(T value) :root(NULL), RefValue(value) 21 { 22 T x; 23 cin >> x; 24 while (x != RefValue) 25 { 26 Insert(x, root); //新建一個結點,調用Insert插入到樹中 27 cin >> x; 28 } 29 } 30 //析構 31 ~BST() { Destroy(root); } 32 33 //插入 34 bool Insert(T x) { return Insert(x, root); } 35 36 //刪除 37 bool Remove(T x) { return Remove(x, root); } 38 39 //搜索 40 bool Search(T x) { return (Search(x, root) != NULL) ? true : false; } 41 42 //中序遍歷 43 void InOrder() { InOrder(root); } 44 45 protected: 46 47 //以ptr為根的二叉搜索樹中插入所含值為e1的結點 48 bool Insert(const T& e1, BSTNode<T>* &ptr) //第二個參數是指針的引用 49 { 50 if (ptr == NULL) 51 { 52 ptr = new BSTNode<T>(e1); //構造新結點 53 if (ptr == NULL) 54 { 55 cout << "Memory allocation failed!" << endl; 56 exit(1); 57 } 58 return true; 59 } 60 else if (e1 < ptr->data) //小於,插入左子樹 61 { 62 Insert(e1, ptr->left); 63 } 64 else if (e1 > ptr->data) //大於,插入右子樹 65 { 66 Insert(e1, ptr->right); 67 } 68 else //x已在樹中,不插入 69 { 70 return false; 71 } 72 } 73 74 //以ptr為根的二叉搜索樹中刪除含x的結點 75 bool Remove(T x, BSTNode<T>* &ptr) 76 { 77 BSTNode<T>* temp; 78 if (ptr != NULL) //ptr不為空進行操作 79 { 80 if (x < ptr->data) 81 { 82 Remove(x, ptr->left); 83 } 84 else if (x > ptr->data) 85 { 86 Remove(x, ptr->right); 87 } 88 //找到了要刪除的結點 89 //1.要刪除的結點ptr同時有左右子樹 90 else if (ptr->left != NULL&&ptr->right != NULL) 91 { 92 temp = ptr->right; //在右子樹中搜索中序下的第一個結點 93 while (temp->left != NULL) 94 { 95 temp = temp->left; 96 } 97 //用右子樹中序下的第一個結點的值填充要刪除的結點 98 ptr->data = temp->data; 99 //然后再新填充值ptr的右子樹中刪除temp的data值 100 Remove(ptr->data, ptr->right); 101 } 102 else //不同時有左右子樹 103 { 104 temp = ptr; //temp記住要刪除的ptr結點 105 if (ptr->left == NULL) //只有右子樹 106 { 107 ptr = ptr->right; 108 } 109 else //只有左子樹 110 { 111 ptr = ptr->left; 112 } 113 delete temp; //刪除結點 114 temp = NULL; 115 return true; 116 } 117 } 118 else //ptr為空直接返回false 119 { 120 return false; 121 } 122 } 123 124 //在ptr為根的二叉搜索樹中搜索含x的結點。若找到,返回該結點地址,否則返回NULL 125 BSTNode<T>* Search(T x, BSTNode<T>* ptr) 126 { 127 if (ptr == NULL) 128 { 129 return NULL; 130 } 131 else if (x < ptr->data) 132 { 133 return Search(x, ptr->left); 134 } 135 else if (x > ptr->data) 136 { 137 return Search(x, ptr->right); 138 } 139 else 140 { 141 return ptr; 142 } 143 } 144 145 //中序遍歷 146 void InOrder(BSTNode<T>* root) 147 { 148 if (root != NULL) 149 { 150 InOrder(root->left); 151 cout << root->data << " "; 152 InOrder(root->right); 153 } 154 } 155 156 //銷毀以root為根的二叉樹搜索樹函數 157 void Destroy(BSTNode<T>* &root) 158 { 159 if (root == NULL) 160 { 161 return; 162 } 163 if (root->left != NULL) 164 { 165 Destroy(root->left); 166 } 167 if (root->right != NULL) 168 { 169 Destroy(root->right); 170 } 171 delete root; 172 root = NULL; 173 } 174 private: 175 BSTNode<T>* root; //根指針 176 T RefValue; //輸入結束標識 177 }; 178 179 int main(int argc, char* argv[]) 180 { 181 //g a e d f h j i l k # 182 BST<char> tree('#'); 183 tree.InOrder(); 184 cout << endl; 185 cout << tree.Search('e') << endl; 186 cout << tree.Insert('z') << endl; 187 tree.InOrder(); 188 cout << endl; 189 cout << tree.Remove('z') << endl; 190 cout << tree.Remove('j') << endl; 191 tree.InOrder(); 192 cout << endl; 193 return 0; 194 }