《算法導論》學習記錄目錄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 }
下圖爲將關鍵字爲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 }
由於二叉查找樹的特殊性質,咱們很容易就能夠想到關鍵字最小的結點的位置必定在樹的最左邊,關鍵字最大的結點必定在樹的最右邊。
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 }
對於結點的前趨(具備小於該結點的關鍵字中最大者的那個結點)、後繼(具備大於該結點的關鍵字中最小者的那個結點)。
用後繼來舉例:
若是該結點存在右兒子,則後繼爲以其右兒子爲根的子樹的最小值對應的結點
若是沒有右兒子,則找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 }
對於結點的刪除,咱們要肯定真正刪除的結點是哪個。
由於(假設要刪除的結點爲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 }
樹遍歷,將樹中的全部關鍵字按特定順序所有輸出。特定順序包括中序(關鍵字介於左右子樹關鍵字之間)、前序(關鍵字位於左右子樹關鍵字以前)、後序(關鍵字位於左右子樹關鍵字以後)。
下列代碼爲原文的中序遞歸遍歷實現:
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 }
下列爲完整代碼:
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 }
由於這段時間比較忙(寫實驗的代碼、作做業、看關於Linux的書、看具體數學、忽然還開始看SICP。。。暈),一直都沒時間寫(好爛的藉口)。。。。改天會將本身以爲應該添加的完善(前序、後序,迭代版本等等)。。。
繼續努力!!!!
博客能更新真的很開心!!!