《算法導論》第十二章----二叉查找樹

《算法導論》學習記錄目錄html

查找樹是一種支持包括查找、插入、找最小值、找出最大值、找出前趨、找出後繼、刪除動態集合操做的數據結構。算法

基本操做的時間與樹的高度成正比,對於一棵含有n個結點的徹底二叉樹,基本操做的最壞狀況運行時間爲Θ(lgn),對於含有n個結點的樹(不是徹底二叉樹),最壞的狀況(線性鏈)運行時間爲Θ(n)。數據結構

二叉查找樹的性質:x爲二叉查找樹的一個結點,x_l 爲x的左子樹中的一個結點,那麼x_l存儲的關鍵字小於或者等於x存儲的關鍵字;x_r爲x的右子樹中的一個結點,那麼x_r存儲的關鍵字大於或者等於x存儲的關鍵字。ide

以下圖所示:函數

 

二叉樹能夠用鏈表結構來表示,每一個結點除了關鍵字和衛星數據外,還有3個指針,分別指向左右兒子結點和父結點。學習

 

關於樹結點的插入:spa

一、樹爲空;這時候要插入到樹的結點爲樹根。3d

二、樹不爲空;從根結點開始,不斷與插入結點的關鍵字比較,若是插入結點比較大,那麼就往根結點的右子樹走,不然就往左子樹走;而後繼續與子樹的根結點比較,直到爲空結點,將要插入的結點插到該位置,並將其父指針指向正確的結點。指針

 

 1 /*
 2  * 插入函數,注意插入結點與其在樹中對應的父結點的連接(須要記錄父結點)。
 3  * 從根結點出發,不停用當前結點與插入的值比較,若是當前結點的值比較大就往當前結點的左兒子走,相反就往右兒子走,直到當前結點爲空,
 4  * 在過程當中記錄當前結點的父結點。
 5  * 運行時間爲O(h),h爲樹的高度。由於整個過程是一條沿着根結點降低的路徑。
 6  */
 7 void Tree_Insert(Tree *T, int key){
 8     TreeNode *x;
 9     x = (TreeNode *)malloc(sizeof(TreeNode));           //新建結點,並將key值付給結點的數據
10     x->value = key;
11     x->parent = x->left = x->right = NULL;
12 
13     if(T->root == NULL)
14         T->root = x;                                    //若是樹爲空,x結點爲根
15     else{
16         TreeNode *y = T->root;                          //y結點用來記錄當前結點
17         TreeNode *z = NULL;                             //z結點用來記錄當前結點的父結點
18         while(y != NULL){
19             z = y;
20             if(y->value > x->value)
21                 y = y->left;
22             else
23                 y = y->right;
24         }
25         x->parent = z;                                  //將x結點與其父結點連接
26         if(z->value > x->value)
27             z->left = x;
28         else
29             z->right = x;                               //x結點的父節點與x結點連接
30     }
31 }
View Code

 

下圖爲將關鍵字爲C的結點插入到二叉查找樹裏:code

 

查詢也差很少,將想要查詢的關鍵字與根結點比較(如同插入操做的第二種狀況),不斷的往下走,直到當前結點的關鍵字等於查詢的關鍵字或者當前結點爲空結點。

 1 /*
 2  * 查找函數,返回含關鍵值對應的結點指針
 3  */
 4 TreeNode * Tree_Search(TreeNode *x, int key){
 5     if(x == NULL || x->value == key)
 6         return x;
 7     
 8     if(x->value > key)
 9         Tree_Search(x->left, key);
10     else
11         Tree_Search(x->right, key);
12 }
View Code

由於二叉查找樹的特殊性質,咱們很容易就能夠想到關鍵字最小的結點的位置必定在樹的最左邊,關鍵字最大的結點必定在樹的最右邊。

 1 /*
 2  * 找最小值,並返回最小值對應的結點指針
 3  */
 4 TreeNode * Tree_Minimum(TreeNode *x){
 5     TreeNode *r = x;
 6     while(r->left != NULL)
 7         r = r->left;
 8     return r;
 9 }
10 
11 /*
12  * 找最大值,並返回最大值對應的結點指針
13  */
14 TreeNode * Tree_Maximum(TreeNode *x){
15     TreeNode *r = x;
16     while(r->right != NULL)
17         r = r->right;
18     return r;
19 }
View Code

對於結點的前趨(具備小於該結點的關鍵字中最大者的那個結點)、後繼(具備大於該結點的關鍵字中最小者的那個結點)。

用後繼來舉例:

若是該結點存在右兒子,則後繼爲以其右兒子爲根的子樹的最小值對應的結點
若是沒有右兒子,則找x結點的最低的祖先結點而且x結點處於最低祖先結點的左兒子子樹裏。若是在右兒子子樹裏,x結點的值比祖先結點的值大。

 1 /*
 2  * 找某個結點的後繼(關鍵字大於該結點中最小的那個結點)
 3  * 若是該結點存在右兒子,則後繼爲以其右兒子爲根的子樹的最小值對應的結點
 4  * 若是沒有右兒子,則找x結點的最低的祖先結點而且x結點處於最低祖先結點的左兒子子樹裏。若是在右兒子子樹裏,x結點的值比祖先結點的值大。
 5  */
 6 TreeNode * Tree_Successor(TreeNode *x){
 7     TreeNode *z = x;
 8     if(z->right != NULL)
 9         return Tree_Minimum(z->right);
10     TreeNode *y = z->parent;
11     while(y!= NULL && z == y->right){
12         z = y;
13         y = y->parent;
14     }
15     return y;
16 }
17 
18 /*
19  * 找某個結點的前趨(關鍵字小於該結點中最大的那個結點)
20  * 與後繼相反
21  */
22 TreeNode * Tree_Predecessor(TreeNode *x){
23     TreeNode *z = x;
24     if(z->left != NULL)
25         return Tree_Maximum(z->left);
26     TreeNode *y = z->parent;
27     while(y != NULL && z == y->left){
28         z = y;
29         y = y->parent;
30     }
31     return y;
32 }
View Code

對於結點的刪除,咱們要肯定真正刪除的結點是哪個。

由於(假設要刪除的結點爲z):若是z沒有左右兒子,那麼咱們就直接用空結點代替它;若是z只有一個兒子結點,就用該結點替代它(如圖a、b);以上兩種狀況都是刪除z結點,可是當z有左右兒子結點的時候,實際上就不是刪除z結點,由於若是直接刪除z結點,z結點與其父結點和兒子結點的聯繫就失去了,樹就不完整了,因此咱們應該找出z結點的後繼,刪除它,再將它的信息替換z的信息(如圖c、d)。

圖c中z結點的後繼爲y結點,由於y結點沒有左兒子結點,因此爲z的右子樹的最小值—後繼。

 1 /*
 2  * 刪除結點函數,首先要肯定真正刪除的結點是那個。
 3  * 若是x沒有子結點,直接將x的父結點對應的指針指向NULL
 4  * 若是x只有一個子節點,直接將x的父結點對應的指針指向x的子結點
 5  * 若是x有兩個子結點,實際上要刪除的不是x,而是x的後繼,,再用x的後繼的內容代替x的內容
 6  */
 7 void Tree_Delete(TreeNode *Root, TreeNode *x){
 8     TreeNode *y;
 9     TreeNode *z;
10     if(x->left == NULL || x->right == NULL)
11         y = x;
12     else
13         y = Tree_Successor(x);
14 
15     if(y->left != NULL) 
16         z = y->left;
17     else
18         z = y->right;
19 
20     if(z != NULL)
21         z->parent = y->parent;
22     if(y->parent == NULL)
23         Root = z;
24     else if(y == y->parent->left)
25         y->parent->left = z;
26     else
27         y->parent->right = z;
28     if(y != x)
29         x->value = y->value;
30 
31     free(y);
32 }
View Code

樹遍歷,將樹中的全部關鍵字按特定順序所有輸出。特定順序包括中序(關鍵字介於左右子樹關鍵字之間)、前序(關鍵字位於左右子樹關鍵字以前)、後序(關鍵字位於左右子樹關鍵字以後)。

下列代碼爲原文的中序遞歸遍歷實現:

 1 /*
 2  * 中序遍歷
 3  * 按排列順序輸出樹中的全部關鍵字
 4  */
 5 void Inorder_Tree_Walk(TreeNode *x){
 6     if(x != NULL){
 7         Inorder_Tree_Walk(x->left);
 8         printf("%d ", x->value);
 9         Inorder_Tree_Walk(x->right);
10     }
11 }
View Code

下列爲完整代碼:

  1 #include <stdio.h>
  2 #include <stdlib.h>
  3 
  4 #define MAX 21
  5 
  6 typedef struct TreeNode{
  7     int value;
  8     struct TreeNode * parent;
  9     struct TreeNode * left;
 10     struct TreeNode * right;
 11 }TreeNode;                              //樹結點結構體,包含數據、左右兒子結點指針、父結點指針
 12 
 13 typedef struct{
 14     TreeNode * root;
 15 }Tree;                                  //樹結構體,包含一個根結點
 16 
 17 void Tree_Insert(Tree *T, int key);     //插入函數
 18 
 19 void Tree_Delete(TreeNode *Root, TreeNode *x);  //刪除函數
 20 
 21 TreeNode * Tree_Search(TreeNode *x, int key);   //查找函數
 22 
 23 void Inorder_Tree_Walk(TreeNode *Root);         //中序遍歷
 24 
 25 void Inorder_Tree_Walk_Iterative(TreeNode *Root);
 26 
 27 TreeNode * Tree_Minimum(TreeNode *x);           //找最小值
 28         
 29 TreeNode * Tree_Maximum(TreeNode *x);           //找最大值
 30 
 31 TreeNode * Tree_Successor(TreeNode *x);         //找後繼
 32 
 33 TreeNode * Tree_Predecessor(TreeNode *x);       //找前趨
 34 
 35 void free_mem(TreeNode *x);                     //釋放內存
 36 
 37 int main(){
 38     Tree *T;
 39     T->root = NULL;
 40 
 41     int n, value, i;
 42     scanf("%d", &n);
 43     for(i = 1; i <= n; i++){
 44         scanf("%d", &value);
 45         Tree_Insert(T, value);
 46     }
 47     TreeNode *s = Tree_Search(T->root, 3);
 48     if(s != NULL)
 49         printf("%d\n", s->value);
 50     Inorder_Tree_Walk(T->root);
 51 
 52     printf("\n");
 53     printf("%d\n", Tree_Minimum(T->root)->value);
 54     printf("%d\n", T->root->value);
 55     printf("%d\n", Tree_Maximum(T->root)->value);
 56 
 57     printf("%d\n", Tree_Successor(s)->value);
 58     printf("%d\n", Tree_Predecessor(s)->value);
 59     Tree_Delete(T->root, s);
 60     Inorder_Tree_Walk(T->root);
 61     printf("\n");
 62 
 63     free_mem(T->root);
 64     return 0;
 65 }
 66 
 67 /*
 68  * 插入函數,注意插入結點與其在樹中對應的父結點的連接(須要記錄父結點)。
 69  * 從根結點出發,不停用當前結點與插入的值比較,若是當前結點的值比較大就往當前結點的左兒子走,相反就往右兒子走,直到當前結點爲空,
 70  * 在過程當中記錄當前結點的父結點。
 71  * 運行時間爲O(h),h爲樹的高度。由於整個過程是一條沿着根結點降低的路徑。
 72  */
 73 void Tree_Insert(Tree *T, int key){
 74     TreeNode *x;
 75     x = (TreeNode *)malloc(sizeof(TreeNode));           //新建結點,並將key值付給結點的數據
 76     x->value = key;
 77     x->parent = x->left = x->right = NULL;
 78 
 79     if(T->root == NULL)
 80         T->root = x;                                    //若是樹爲空,x結點爲根
 81     else{
 82         TreeNode *y = T->root;                          //y結點用來記錄當前結點
 83         TreeNode *z = NULL;                             //z結點用來記錄當前結點的父結點
 84         while(y != NULL){
 85             z = y;
 86             if(y->value > x->value)
 87                 y = y->left;
 88             else
 89                 y = y->right;
 90         }
 91         x->parent = z;                                  //將x結點與其父結點連接
 92         if(z->value > x->value)
 93             z->left = x;
 94         else
 95             z->right = x;                               //x結點的父節點與x結點連接
 96     }
 97 }
 98 
 99 /*
100  * 查找函數,返回含關鍵值對應的結點指針
101  */
102 TreeNode * Tree_Search(TreeNode *x, int key){
103     if(x == NULL || x->value == key)
104         return x;
105     
106     if(x->value > key)
107         Tree_Search(x->left, key);
108     else
109         Tree_Search(x->right, key);
110 }
111 
112 /*
113  * 找最小值,並返回最小值對應的結點指針
114  */
115 TreeNode * Tree_Minimum(TreeNode *x){
116     TreeNode *r = x;
117     while(r->left != NULL)
118         r = r->left;
119     return r;
120 }
121 
122 /*
123  * 找最大值,並返回最大值對應的結點指針
124  */
125 TreeNode * Tree_Maximum(TreeNode *x){
126     TreeNode *r = x;
127     while(r->right != NULL)
128         r = r->right;
129     return r;
130 }
131 
132 /*
133  * 找某個結點的後繼(關鍵字大於該結點中最小的那個結點)
134  * 若是該結點存在右兒子,則後繼爲以其右兒子爲根的子樹的最小值對應的結點
135  * 若是沒有右兒子,則找x結點的最低的祖先結點而且x結點處於最低祖先結點的左兒子子樹裏。若是在右兒子子樹裏,x結點的值比祖先結點的值大。
136  */
137 TreeNode * Tree_Successor(TreeNode *x){
138     TreeNode *z = x;
139     if(z->right != NULL)
140         return Tree_Minimum(z->right);
141     TreeNode *y = z->parent;
142     while(y!= NULL && z == y->right){
143         z = y;
144         y = y->parent;
145     }
146     return y;
147 }
148 
149 /*
150  * 找某個結點的前趨(關鍵字小於該結點中最大的那個結點)
151  * 與後繼相反
152  */
153 TreeNode * Tree_Predecessor(TreeNode *x){
154     TreeNode *z = x;
155     if(z->left != NULL)
156         return Tree_Maximum(z->left);
157     TreeNode *y = z->parent;
158     while(y != NULL && z == y->left){
159         z = y;
160         y = y->parent;
161     }
162     return y;
163 }
164 
165 /*
166  * 中序遍歷
167  * 按排列順序輸出樹中的全部關鍵字
168  */
169 void Inorder_Tree_Walk(TreeNode *x){
170     if(x != NULL){
171         Inorder_Tree_Walk(x->left);
172         printf("%d ", x->value);
173         Inorder_Tree_Walk(x->right);
174     }
175 }
176 
177 /*
178  * 刪除結點函數,首先要肯定真正刪除的結點是那個。
179  * 若是x沒有子結點,直接將x的父結點對應的指針指向NULL
180  * 若是x只有一個子節點,直接將x的父結點對應的指針指向x的子結點
181  * 若是x有兩個子結點,實際上要刪除的不是x,而是x的後繼,,再用x的後繼的內容代替x的內容
182  */
183 void Tree_Delete(TreeNode *Root, TreeNode *x){
184     TreeNode *y;
185     TreeNode *z;
186     if(x->left == NULL || x->right == NULL)
187         y = x;
188     else
189         y = Tree_Successor(x);
190 
191     if(y->left != NULL) 
192         z = y->left;
193     else
194         z = y->right;
195 
196     if(z != NULL)
197         z->parent = y->parent;
198     if(y->parent == NULL)
199         Root = z;
200     else if(y == y->parent->left)
201         y->parent->left = z;
202     else
203         y->parent->right = z;
204     if(y != x)
205         x->value = y->value;
206 
207     free(y);
208 }
209 
210 void free_mem(TreeNode *x){
211     if(x != NULL){
212         free_mem(x->left);
213         free_mem(x->right);
214         free(x);
215     }
216 }
View Code

由於這段時間比較忙(寫實驗的代碼、作做業、看關於Linux的書、看具體數學、忽然還開始看SICP。。。暈),一直都沒時間寫(好爛的藉口)。。。。改天會將本身以爲應該添加的完善(前序、後序,迭代版本等等)。。。

繼續努力!!!!

博客能更新真的很開心!!!

相關文章
相關標籤/搜索