【二叉搜索樹】的詳細實現(C++)


二叉搜索樹的概念

  從前面討論折半搜索的性能中可知,如果每次從搜索序列的中間進行搜索,把區間縮小一半,通過有限次迭代,很快就能通近到所要尋找的元素。進一步,如果我們直接輸入搜索序列,構造出類似於折半搜索的判定樹那樣的樹形結構,就能實現快速搜索。這種樹形結構就是二又搜索樹。
二又搜索樹(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 }
源代碼

 


免責聲明!

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



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