0.序
本人現讀本科大二,這學期學習數據結構,老師為我們的期末作業布置一道任選題,而我一直以來都有聽說B樹是一棵挺神奇的樹,所以我選擇了它,當然更重要的原因是因為B樹的難度最高,我喜歡做有挑戰性的工作。同時,我聽我基友說他熱衷於將自己所學所想分享到博客園上,故才有了這樣一篇文章。希望我能夠在寫博客的同時學習到更多東西,同時也能幫助到其他遇到或者即將遇到雷同問題的初學者們。
1.關於B樹
B樹是一種稱之為查找樹的樹,與之類似的有查找二叉樹,平衡二叉樹,除此之外,還有各種B樹的兄弟姐妹B+樹、B-樹、B*樹等,他們共同的特點就是都是按照一定的順序規律存儲的。B樹的應用也是很廣泛的,B樹是幾乎所有數據庫的默認索引結構,也是用的最多的索引結構。B樹是一種多叉樹,根據其最多叉的數目可以直接稱為M階B樹。根據算法導論上敘述,還可按B樹每個節點最少的分支確定一棵B樹的階,但是這樣的話B樹的階便必須為偶數。我個人是使用根據最大分支的數目確定B樹的階的。
下圖就是某一棵B樹,每一個結點中都包含有數個數據元素,而同時一定會有數據元素的個數加一個的子樹。
一棵M階B樹或為空樹,或為滿足下列特性的M叉樹:
(1)樹中每個結點最多含有M棵子樹;
(2)若根結點不是葉子結點,則至少有2棵子樹;
(3)除根結點之外的所有非終端結點至少有[m/2]棵子樹;
(4)每個非終端結點中包含的信息keynum,ptr[0],key[1],ptr[1],key[2],ptr[2],……key[keynum],ptr[keynum];
其中,key為關鍵字,且關鍵字按升序排序,ptr為指向子樹的根結點指針
2.思路及實現
B樹的接口主要是插入(包括空樹插入一個元素)和刪除操作,而插入和刪除操作不可避免的都會用到查找操作,而查找的主要思路比較簡單,主要是利用B樹是一種排序樹的原理,可以很快找到要插入位置或者要刪除結點。這里的關鍵是注意返回內容包括查找結果所在結點,以及該元素所在位置,這是為了方便接下來的操作比較簡單。
插入:
通過對B樹進行遍歷,找出要插入的結點以及結點位置,如果找到的key值在B樹當中已經存在,則說明插入失敗,否則,就可以進行插入操作。這里可以先不管是否超出M階樹的上限要求,因為我們在定義的時候會故意留下一個位置,可以存放多余的一個元素,插入之后,通過判斷是否達到M階樹上限要求,再進行遞歸的分裂操作。
1 /*** 2 * @name Status insertBTree(BTree &T, Record e) 3 * @description 插入實現元素的插入 4 * @return 成功返回OK,如果存在則返回FALSE,否則返回ERROR 5 * @notice 6 ***/ 7 Status insertBTree(BTree &T, Record e) 8 { 9 BTree p; 10 int index, temp; 11 Status find_flag; 12 if (NULL == T)//考慮B樹為空樹的情況 13 { 14 T = (BTree)malloc(BTLEN); 15 if (NULL == T) return OVERFLOW; 16 T->keynum = 1; 17 T->parent = NULL; 18 for (index = 0;index <= m; ++index) 19 { 20 T->ptr[index] = NULL; 21 T->key[index] = 0; 22 } 23 T->key[1] = e.key; 24 return OK; 25 } 26 find_flag = findBTree(T, p, temp, e.key);//尋找插入節點 27 if (find_flag == TRUE) 28 { 29 return FALSE; 30 } 31 if (find_flag == FALSE) 32 { //不管怎樣先直接插入 33 p->keynum++; 34 for (index = p->keynum;index > temp;--index) 35 { 36 p->key[index] = p->key[index - 1]; 37 p->ptr[index] = p->ptr[index - 1]; 38 } 39 p->ptr[temp] = NULL; 40 p->key[temp] = e.key; 41 if (p->keynum == m) //這種情況得分裂 42 { 43 splitBTree(p); 44 } 45 renewParent(T); 46 return OK; 47 } 48 return ERROR; 49 }
分裂:
分裂操作是插入操作過程中一個最重要的操作,因為這是處理“沖突”(即結點中的數據元素大於B樹規則中要求的最大個數)的一個通用的處理方式,這種方式必須要對所有的情況都適用,而分裂是解決這一問題一個方法。當然這種方法只是考慮到效率,沒有對兄弟可否借數據進行判斷,但是另外一種方式比較麻煩,這里先不做討論。
分裂的思路是讓父親結點先騰出一個位置(包括key和ptr)出來,然后在需要分裂的結點里面取中間的元素並且移動中間的元素key到父親結點已經騰出來的key位置那里,然后把分裂出來的右部分接到騰出來的ptr那里。注意整個過程對左部分和右部分的都要改變元素的個數以及清空一些沒用的空間。在往上分裂之后可能會造成一種情況,就是父親結點也可能達到分裂的最大個數,所以,檢查父親結點是否需要分裂,需要的話,遞歸之。
1 /*** 2 * @name status splitBTree(BTree T) 3 * @description 遞歸實現分裂節點操作 4 * @return 成功返回OK,否則返回ERROR 5 * @notice 6 ***/ 7 Status splitBTree(BTree T) //此時分裂的節點一定會是超出最大值的。 8 { 9 BTree t1, t2; 10 int index, index_1; 11 if (T->parent == NULL) 12 { 13 t1 = (BTree)malloc(BTLEN); 14 if (NULL == t1) return OVERFLOW; 15 t2 = (BTree)malloc(BTLEN); 16 if (NULL == t2) return OVERFLOW; 17 18 t1->keynum = m / 2; 19 t2->keynum = m - (m / 2) - 1; 20 t1->parent = T; 21 t2->parent = T; 22 for (index = 0;index <= m; ++index) //先全部初始化 23 { 24 t1->ptr[index] = NULL; 25 t1->key[index] = 0; 26 t2->ptr[index] = NULL; 27 t2->key[index] = 0; 28 } 29 for (index = 0;index <= m / 2; ++index) //初始化t1 30 { 31 t1->ptr[index] = T->ptr[index]; 32 t1->key[index] = T->key[index]; 33 } 34 t2->ptr[0] = T->ptr[(m / 2) + 1]; 35 for (index = (m / 2) + 2;index <= m; ++index) //初始化t2 36 { 37 t2->ptr[index - ((m / 2) + 1)] = T->ptr[index]; 38 t2->key[index - ((m / 2) + 1)] = T->key[index]; 39 } 40 T->keynum = 1; 41 T->ptr[0] = t1; 42 T->ptr[1] = t2; 43 T->key[1] = T->key[m / 2 + 1]; 44 for (index = 2;index <= m; ++index) //初始化T 45 { 46 T->ptr[index] = NULL; 47 T->key[index] = 0; 48 } 49 return OK; 50 }
刪除:
B樹元素的刪除操作與插入操作類似,但是卻要麻煩,因為得分兩種情況處理。(1)尋找到存在這個元素,而且這個元素所在是葉子節點(即它的孩子為空),直接對其進行刪除,之后再判斷是否小於B樹規則中要求的最小的子樹個數。如果小於,那就調用合並函數。(2)如果尋找到的這個元素是非葉子節點的元素,通過尋找比該元素小的最大元素(該元素肯定為葉子節點),把該元素直接賦值給要刪除的元素,再在葉子節點處進行(1)中的操作。
1 /*** 2 * @name Status deleteBTreeRecord(BTree &T, Record e) 3 * @description 實現B樹元素的刪除 4 * @return 成功返回OK,否則返回ERROR 5 * @notice 6 ***/ 7 Status deleteBTreeRecord(BTree &T, Record e) 8 { 9 BTree p, q; 10 int num, temp, index; 11 Status find_flag; 12 if (T == NULL) 13 return ERROR; 14 find_flag = findBTree(T, p, temp, e.key); 15 if (find_flag == FALSE) 16 { 17 return FALSE; 18 } 19 if (find_flag == TRUE) 20 { 21 //deleteBTreeBNode(p,temp); 22 if (p->ptr[temp] == NULL) //如果是葉子節點的話 23 { 24 for (index = temp;index <= p->keynum;++index) 25 { 26 p->key[index] = p->key[index + 1]; 27 p->ptr[index] = p->ptr[index + 1]; 28 } 29 p->keynum--; 30 if (p->keynum == (m + 1) / 2 - 2) 31 { 32 //調用借兄弟的函數 33 if (borrowBNode(p) == EMPTY) T = NULL; 34 else renewParent(T); 35 } 36 return OK; 37 } 38 else //不是葉子結點的話 39 { 40 //遍歷 41 findMax(p->ptr[temp - 1], q, num);//返回的q一定會是葉子節點 42 p->key[temp] = q->key[num]; 43 q->key[num] = 0; 44 q->keynum--; 45 if (q->keynum == (m + 1) / 2 - 2) 46 { 47 //調用借兄弟的函數 48 if (borrowBNode(q) == EMPTY) T = NULL; 49 else renewParent(T); 50 } 51 return OK; 52 } 53 return OK; 54 } 55 return ERROR; 56 }
合並:
在此先聲明,因為一開始只考慮B樹的階為4的情況,后來改為使用宏定義階M的數值,所以這段代碼存在BUG,只支持階為3或4的B樹= =。
思路還是挺清晰的,首先先向兄弟結點借元素,如果兄弟能夠借給你元素的話(即借了你之后並不會小於最少的分支),那么直接從兄弟那里取元素,否則,和兄弟合並。
合並其實是分裂反過來的情況,從父親結點那里取出一個key值介於要合並的兩個結點之間的元素,插入左部分最末尾處,同時右部分插到左部分后面,然后父親結點元素依次往前挪。從而實現合並操作。之后,也必須對父親結點進行判斷是否小於最小的分支數,如果也小於,對父親節點進行遞歸操作。
1 /*** 2 * @name Status borrowBNode(BTree &T) 3 * @description 遞歸實現,向兄弟借元素,否則和兄弟合並 4 * @return 成功返回OK,否則返回ERROR 5 * @notice 這種情況應該是T為單元素結點 6 ***/ 7 Status borrowBNode(BTree T) 8 { 9 int mynum, bronum, index; 10 BTree b = NULL, f = NULL; 11 if (T == NULL) return ERROR; 12 f = T->parent; 13 if (f == NULL)//考慮父親結點不存在的情況 14 { 15 if (T->keynum == 0) 16 { 17 f = T->ptr[0]; 18 if (f == NULL) 19 { 20 free(T); 21 return EMPTY; 22 } 23 for (index = 0;index <= f->keynum;index++) 24 { 25 T->key[index] = f->key[index]; 26 T->ptr[index] = f->ptr[index]; 27 } 28 T->keynum = f->keynum; 29 free(f); 30 renewParent(T); 31 } 32 return OK; 33 } 34 mynum = whichSon(T); 35 if (mynum == 0) 36 bronum = 1; 37 else 38 bronum = mynum - 1; 39 b = f->ptr[bronum]; 40 if (b->keynum == (m + 1) / 2 - 1) //如果兄弟幫不了你了 41 { 42 //那么就和這個兄弟合體 43 if (bronum < mynum) //如果我不是第一個 44 { 45 b->keynum++; 46 b->key[b->keynum] = f->key[mynum]; 47 b->ptr[b->keynum] = T->ptr[0]; 48 for (index = 1;index <= T->keynum;index++) 49 { 50 b->key[index + b->keynum] = T->key[index]; 51 b->ptr[index + b->keynum] = T->ptr[index]; 52 b->keynum++; 53 } 54 free(T); 55 for (index = mynum;index <= f->keynum;index++) 56 { 57 f->key[index] = f->key[index + 1]; 58 f->ptr[index] = f->ptr[index + 1]; 59 } 60 f->keynum--; 61 } 62 else 63 { 64 T->keynum++; 65 T->key[T->keynum] = f->key[bronum]; 66 T->ptr[T->keynum] = b->ptr[0]; 67 for (index = 1;index <= b->keynum;index++) 68 { 69 T->key[index + T->keynum] = b->key[index]; 70 T->ptr[index + T->keynum] = b->ptr[index]; 71 T->keynum++; 72 } 73 free(b); 74 for (index = bronum;index <= f->keynum;index++) 75 { 76 f->key[index] = f->key[index + 1]; 77 f->ptr[index] = f->ptr[index + 1]; 78 } 79 f->keynum--; 80 } 81 renewParent(f); 82 if (f->keynum == (m + 1) / 2 - 2) 83 { 84 //調用借兄弟的函數 85 return borrowBNode(f); 86 } 87 } 88 else//如果兄弟能夠幫你 89 { 90 if (bronum < mynum) //如果我不是第一個 91 { 92 for (index = 1;index <= T->keynum;index++) 93 { 94 T->key[index + 1] = T->key[index]; 95 T->ptr[index + 1] = T->ptr[index]; 96 } 97 T->ptr[1] = T->ptr[0]; 98 T->key[1] = f->key[mynum]; 99 T->ptr[0] = b->ptr[b->keynum]; 100 T->keynum++; 101 f->key[mynum] = b->key[b->keynum]; 102 b->key[b->keynum] = 0; 103 b->ptr[b->keynum] = NULL; 104 b->keynum--; 105 106 } 107 else //如果我是第一個 108 { 109 T->keynum++; 110 T->key[T->keynum] = f->key[1]; 111 T->ptr[T->keynum] = b->ptr[0]; 112 f->key[1] = b->key[1]; 113 b->ptr[0] = b->ptr[1]; 114 for (index = 1;index <= b->keynum;index++) 115 { 116 b->key[index] = b->key[index + 1]; 117 b->ptr[index] = b->ptr[index + 1]; 118 } 119 b->keynum--; 120 } 121 } 122 return OK; 123 }
遍歷,輸出:
為了讓B樹更容易看,代碼更容易調試,我同時還用隊列寫了個層次遍歷,這個看看就好,實現起來挺麻煩的。而且可能代碼實現也存在問題
1 /*** 2 * @name Status ergodic(BTree T, LinkList L, int newline, int sum) 3 * @description print需要用到的遞歸遍歷程序 4 * @return 成功返回OK 5 * @notice 此處用到隊列 6 ***/ 7 Status ergodic(BTree T, LinkList L, int newline, int sum) 8 { 9 int index; 10 BTree p; 11 if (T != NULL) 12 { 13 printf("[ "); 14 Enqueue_L(L, T->ptr[0]); 15 for (index = 1;index <= T->keynum; index++) 16 { 17 printf("%d ", T->key[index]); 18 Enqueue_L(L, T->ptr[index]); 19 } 20 sum += T->keynum + 1; 21 printf("]"); 22 if (newline == 0) 23 { 24 printf("\n"); 25 newline = sum - 1; 26 sum = 0; 27 } 28 else 29 { 30 --newline; 31 } 32 } 33 if (IfEmpty(L) == FALSE) 34 { 35 Dequeue_L(L, p); 36 ergodic(p, L, newline, sum); 37 } 38 return OK; 39 } 40 /*** 41 * @name Status print(BTree T) 42 * @description 層次遍歷並分層輸出B樹 43 * @return 成功返回OK 44 * @notice 45 ***/ 46 Status print(BTree T) 47 { 48 LinkList L; 49 if (T == NULL) 50 { 51 printf("[ ]\n"); 52 return OK; 53 } 54 InitQueue_L(L); 55 ergodic(T, L, 0, 0); 56 DestroyQueue(L); 57 return OK; 58 }
3.測試
4.總結
以目前所掌握的知識,終於把B樹做出來了,整個過程沒有參考過其他人的代碼,所以並不知道自己的一些思路是否得當,如有錯誤,多多包涵。在整個過程中,最難,卡的最久的也就是合並操作了,這塊的代碼也是亂得掉渣,以后有時間把他完善了。最后附上完整代碼。

1 #define _CRT_SECURE_NO_WARNINGS 2 #include<stdio.h> 3 #include<stdlib.h> 4 #include<time.h> 5 #define BTREELENGTH 50 6 #define BTLEN (sizeof(BTNode)) 7 #define MAXINT 100 8 typedef enum status 9 { 10 TRUE, 11 FALSE, 12 OK, 13 ERROR, 14 OVERFLOW, 15 EMPTY 16 }Status; 17 typedef int KeyType; 18 19 //**********************************B樹**************************************** 20 #define m 3 // B樹的階,此設為4 21 typedef struct 22 { 23 KeyType key; 24 char data; 25 } Record; 26 typedef struct BTNode 27 { 28 int keynum; // 結點中關鍵字個數,即結點的大小 29 struct BTNode *parent; // 指向雙親結點 30 KeyType key[m + 1]; // 關鍵字向量,0號單元未用 31 struct BTNode *ptr[m + 1]; // 子樹指針向量 32 // Record *recptr[m + 1]; // 記錄指針向量,0號單元未用 33 //在此添加其他自定義數據 34 } BTNode, *BTree; // B樹結點和B樹的類型 35 typedef struct 36 { 37 BTNode *pt; // 指向找到的結點 38 int i; // 1..m,在結點中的關鍵字序號 39 int tag; // 1:查找成功,0:查找失敗 40 } Result; // 在B樹的查找結果類型 41 //**********************************B樹**************************************** 42 43 //**********************************隊列*************************************** 44 typedef struct LNode { 45 BTree data; // 數據域 46 struct LNode *next; // 指針域 47 } LNode, *LinkList; 48 //**********************************隊列*************************************** 49 50 /*** 51 * @name Status InitQueue_L(LinkList &L) 52 * @description 初始化隊列 53 * @return 成功返回OK,開辟空間失敗返回OVERFLOW 54 * @notice 55 ***/ 56 Status InitQueue_L(LinkList &L) 57 { // 初始化一個只含頭結點的空單鏈表L 58 if (NULL == (L = (LNode*)malloc(sizeof(LNode)))) // 生成新結點 59 return OVERFLOW; 60 L->next = NULL; 61 return OK; 62 } 63 /*** 64 * @name LNode* MakeNode_L(BTree e) 65 * @description 構造隊列結點 66 * @return 返回結點地址 67 * @notice 68 ***/ 69 LNode* MakeNode_L(BTree e) 70 { // 構造數據域為e的單鏈表結點 71 LNode *p; 72 p = (LNode*)malloc(sizeof(LNode)); // 分配結點空間 73 if (p != NULL) 74 { 75 p->data = e; 76 p->next = NULL; 77 } 78 return p; 79 } 80 /*** 81 * @name Status Enqueue_L(LNode *p, BTree e) 82 * @description 隊列的入隊 83 * @return 成功返回OK,否則返回ERROR 84 * @notice 85 ***/ 86 Status Enqueue_L(LNode *p, BTree e) 87 { //在p結點之后插入q結點 88 if (NULL == p) return ERROR; // 參數不合理 89 while (p->next != NULL) 90 p = p->next; 91 p->next = MakeNode_L(e); // 對應圖4.11(b)的②,修改p結點的指針域 92 return OK; 93 } 94 95 /*** 96 * @name Status Dequeue_L(LNode *p, BTree &e) 97 * @description 隊列的出隊 98 * @return 成功返回OK,否則返回ERROR 99 * @notice 100 ***/ 101 Status Dequeue_L(LNode *p, BTree &e) 102 { 103 // 刪除p結點的直接后繼結點並用參數e返回被刪結點的值 104 LNode *q; 105 if (NULL == p || NULL == p->next) return ERROR; // 刪除位置不合理 106 q = p->next; 107 p->next = q->next; // 修改被刪結點q的指針域 108 e = q->data; 109 free(q); // 釋放結點q 110 return OK; 111 } 112 113 /*** 114 * @name void DestroyQueue(LinkList L) 115 * @description 隊列的銷毀 116 * @return 無返回 117 * @notice 118 ***/ 119 void DestroyQueue(LinkList L) 120 { 121 // 銷毀整個鏈表 122 LinkList p; 123 if (L != NULL) 124 { 125 p = L; 126 L = L->next; 127 free(p); 128 DestroyQueue(L); 129 } 130 } 131 /*** 132 * @name Status IfEmpty(LinkList L) 133 * @description 判斷隊列是否為空 134 * @return 空返回TRUE,不空返回FALSE,否則返回ERROR 135 * @notice 136 ***/ 137 Status IfEmpty(LinkList L) 138 { 139 if (L == NULL) return ERROR; 140 if (L->next == NULL) return TRUE; 141 return FALSE; 142 } 143 /*** 144 * @name Status ergodic(BTree T, LinkList L, int newline, int sum) 145 * @description print需要用到的遞歸遍歷程序 146 * @return 成功返回OK 147 * @notice 此處用到隊列 148 ***/ 149 Status ergodic(BTree T, LinkList L, int newline, int sum) 150 { 151 int index; 152 BTree p; 153 if (T != NULL) 154 { 155 printf("[ "); 156 Enqueue_L(L, T->ptr[0]); 157 for (index = 1;index <= T->keynum; index++) 158 { 159 printf("%d ", T->key[index]); 160 Enqueue_L(L, T->ptr[index]); 161 } 162 sum += T->keynum + 1; 163 printf("]"); 164 if (newline == 0) 165 { 166 printf("\n"); 167 newline = sum - 1; 168 sum = 0; 169 } 170 else 171 { 172 --newline; 173 } 174 } 175 if (IfEmpty(L) == FALSE) 176 { 177 Dequeue_L(L, p); 178 ergodic(p, L, newline, sum); 179 } 180 return OK; 181 } 182 /*** 183 * @name Status print(BTree T) 184 * @description 層次遍歷並分層輸出B樹 185 * @return 成功返回OK 186 * @notice 187 ***/ 188 Status print(BTree T) 189 { 190 LinkList L; 191 if (T == NULL) 192 { 193 printf("[ ]\n"); 194 return OK; 195 } 196 InitQueue_L(L); 197 ergodic(T, L, 0, 0); 198 DestroyQueue(L); 199 return OK; 200 } 201 202 /*** 203 * @name Status findMax(BTree T, BTree &p,int ans) 204 * @description 尋找最大關鍵字的結點,T為要尋找的樹,p為返回的節點,ans為第幾個 205 * @return 成功返回OK,否則返回ERROR 206 * @notice 207 ***/ 208 Status findMax(BTree T, BTree &p, int &ans) 209 { 210 if (T == NULL) 211 return ERROR; 212 p = T; 213 while (p->ptr[p->keynum] != NULL) 214 { 215 p = p->ptr[p->keynum]; 216 } 217 ans = p->keynum; 218 return OK; 219 } 220 /*** 221 * @name Status findMin(BTree T, BTree &p,int ans) 222 * @description 尋找最小關鍵字的結點,T為要尋找的樹,p為返回的節點,ans為第幾個 223 * @return 成功返回OK,否則返回ERROR 224 * @notice 225 ***/ 226 /*** 227 * @name Status findBTree(BTree T, BTree &p, int &ans, KeyType k) 228 * @description 尋找 ,T為要尋找的樹,p為返回的節點,ans為第幾個元素,k為要找的值 229 * @return 成功返回OK,否則返回ERROR 230 * @notice 231 ***/ 232 Status findBTree(BTree T, BTree &p, int &ans, KeyType k) 233 { 234 BTree q; 235 int index = 1; 236 KeyType keynow; 237 if (T == NULL) 238 return ERROR; 239 q = T; 240 keynow = T->key[1]; 241 while (q != NULL) //深度的遍歷 242 { 243 index = 1; 244 keynow = q->key[index]; 245 while (index <= q->keynum) //節點內對各真值進行遍歷 246 { 247 if (k == keynow) //找到元素 248 { 249 p = q; 250 ans = index; 251 return TRUE; 252 } 253 if (k > keynow) 254 { 255 if (index == q->keynum) 256 { 257 if (q->ptr[index] == NULL) 258 { 259 p = q; 260 ans = q->keynum + 1; 261 return FALSE; 262 } 263 q = q->ptr[index]; 264 break; 265 } 266 ++index; 267 keynow = q->key[index]; 268 continue; 269 } 270 if (k < keynow) 271 { 272 if (q->ptr[index - 1] == NULL) 273 { 274 p = q; 275 ans = index; 276 return FALSE; 277 } 278 q = q->ptr[index - 1]; 279 break; 280 } 281 } 282 } 283 284 return ERROR; 285 } 286 /*** 287 * @name Status renewParent(BTree p) 288 * @description 告訴孩子們親身爸爸是誰 289 * @return 成功返回OK,否則返回ERROR 290 * @notice 291 ***/ 292 Status renewParent(BTree p) 293 { 294 int index; 295 if (p == NULL) return ERROR; 296 for (index = 0;index <= p->keynum;++index) 297 { 298 if (p->ptr[index] != NULL) 299 { 300 p->ptr[index]->parent = p; 301 renewParent(p->ptr[index]); 302 } 303 } 304 return OK; 305 } 306 /*** 307 * @name int whichSon(BTree T) 308 * @description 找出是父親的第幾個孩子 309 * @return 成功返回第幾個孩子,否則返回-1 310 * @notice 311 ***/ 312 int whichSon(BTree T) 313 { 314 int index = -1; 315 if (T == NULL) return -1; 316 for (index = 0;index <= T->parent->keynum;++index) //找出是父親的第幾個孩子 317 { 318 if (T->parent->ptr[index] == T) return index; 319 } 320 return -1; 321 } 322 /*** 323 * @name status splitBTree(BTree T) 324 * @description 遞歸實現分裂節點操作 325 * @return 成功返回OK,否則返回ERROR 326 * @notice 327 ***/ 328 Status splitBTree(BTree T) //此時分裂的節點一定會是超出最大值的。 329 { 330 BTree t1, t2; 331 int index, index_1; 332 if (T->parent == NULL) 333 { 334 t1 = (BTree)malloc(BTLEN); 335 if (NULL == t1) return OVERFLOW; 336 t2 = (BTree)malloc(BTLEN); 337 if (NULL == t2) return OVERFLOW; 338 339 t1->keynum = m / 2; 340 t2->keynum = m - (m / 2) - 1; 341 t1->parent = T; 342 t2->parent = T; 343 for (index = 0;index <= m; ++index) //先全部初始化 344 { 345 t1->ptr[index] = NULL; 346 t1->key[index] = 0; 347 t2->ptr[index] = NULL; 348 t2->key[index] = 0; 349 } 350 for (index = 0;index <= m / 2; ++index) //初始化t1 351 { 352 t1->ptr[index] = T->ptr[index]; 353 t1->key[index] = T->key[index]; 354 } 355 t2->ptr[0] = T->ptr[(m / 2) + 1]; 356 for (index = (m / 2) + 2;index <= m; ++index) //初始化t2 357 { 358 t2->ptr[index - ((m / 2) + 1)] = T->ptr[index]; 359 t2->key[index - ((m / 2) + 1)] = T->key[index]; 360 } 361 T->keynum = 1; 362 T->ptr[0] = t1; 363 T->ptr[1] = t2; 364 T->key[1] = T->key[m / 2 + 1]; 365 for (index = 2;index <= m; ++index) //初始化T 366 { 367 T->ptr[index] = NULL; 368 T->key[index] = 0; 369 } 370 return OK; 371 } 372 373 index = whichSon(T); 374 for (index_1 = T->parent->keynum;index_1 > index;--index_1) //騰出父親的位置 375 { 376 T->parent->ptr[index_1 + 1] = T->parent->ptr[index_1]; 377 T->parent->key[index_1 + 1] = T->parent->key[index_1]; 378 } 379 T->parent->keynum++; 380 T->parent->key[index + 1] = T->key[m / 2 + 1]; 381 t2 = T->parent->ptr[index + 1] = (BTree)malloc(BTLEN); 382 if (NULL == t2) return OVERFLOW; 383 for (index = 0;index <= m; ++index) //先全部初始化 384 { 385 t2->ptr[index] = NULL; 386 t2->key[index] = 0; 387 } 388 t2->keynum = m - (m / 2) - 1; 389 t2->parent = T->parent; 390 t2->ptr[0] = T->ptr[(m / 2) + 1]; 391 for (index = (m / 2) + 2;index <= m; ++index) //初始化t2 392 { 393 t2->ptr[index - ((m / 2) + 1)] = T->ptr[index]; 394 t2->key[index - ((m / 2) + 1)] = T->key[index]; 395 } 396 T->keynum = m / 2; 397 for (index = (m / 2) + 1;index <= m; ++index) //初始化t2 398 { 399 T->ptr[index] = NULL; 400 T->key[index] = 0; 401 } 402 if (T->parent->keynum == m) 403 { 404 splitBTree(T->parent); 405 } 406 return OK; 407 } 408 /*** 409 * @name Status insertBTree(BTree &T, Record e) 410 * @description 插入實現元素的插入 411 * @return 成功返回OK,如果存在則返回FALSE,否則返回ERROR 412 * @notice 413 ***/ 414 Status insertBTree(BTree &T, Record e) 415 { 416 BTree p; 417 int index, temp; 418 Status find_flag; 419 if (NULL == T) 420 { 421 T = (BTree)malloc(BTLEN); 422 if (NULL == T) return OVERFLOW; 423 T->keynum = 1; 424 T->parent = NULL; 425 for (index = 0;index <= m; ++index) 426 { 427 T->ptr[index] = NULL; 428 T->key[index] = 0; 429 } 430 T->key[1] = e.key; 431 return OK; 432 } 433 find_flag = findBTree(T, p, temp, e.key); 434 if (find_flag == TRUE) 435 { 436 return FALSE; 437 } 438 if (find_flag == FALSE) 439 { //不管怎樣先直接插入 440 p->keynum++; 441 for (index = p->keynum;index > temp;--index) 442 { 443 p->key[index] = p->key[index - 1]; 444 p->ptr[index] = p->ptr[index - 1]; 445 } 446 p->ptr[temp] = NULL; 447 p->key[temp] = e.key; 448 if (p->keynum == m) //這種情況得分裂 449 { 450 splitBTree(p); 451 } 452 renewParent(T); 453 return OK; 454 } 455 return ERROR; 456 } 457 /*** 458 * @name Status borrowBNode(BTree &T) 459 * @description 遞歸實現,向兄弟借元素,否則和兄弟合並 460 * @return 成功返回OK,否則返回ERROR 461 * @notice 這種情況應該是T為單元素結點 462 ***/ 463 Status borrowBNode(BTree T) 464 { 465 int mynum, bronum, index; 466 BTree b = NULL, f = NULL; 467 if (T == NULL) return ERROR; 468 f = T->parent; 469 if (f == NULL)//考慮父親結點不存在的情況 470 { 471 if (T->keynum == 0) 472 { 473 f = T->ptr[0]; 474 if (f == NULL) 475 { 476 free(T); 477 return EMPTY; 478 } 479 for (index = 0;index <= f->keynum;index++) 480 { 481 T->key[index] = f->key[index]; 482 T->ptr[index] = f->ptr[index]; 483 } 484 T->keynum = f->keynum; 485 free(f); 486 renewParent(T); 487 } 488 return OK; 489 } 490 mynum = whichSon(T); 491 if (mynum == 0) 492 bronum = 1; 493 else 494 bronum = mynum - 1; 495 b = f->ptr[bronum]; 496 if (b->keynum == (m + 1) / 2 - 1) //如果兄弟幫不了你了 497 { 498 //那么就和這個兄弟合體 499 if (bronum < mynum) //如果我不是第一個 500 { 501 b->keynum++; 502 b->key[b->keynum] = f->key[mynum]; 503 b->ptr[b->keynum] = T->ptr[0]; 504 for (index = 1;index <= T->keynum;index++) 505 { 506 b->key[index + b->keynum] = T->key[index]; 507 b->ptr[index + b->keynum] = T->ptr[index]; 508 b->keynum++; 509 } 510 free(T); 511 for (index = mynum;index <= f->keynum;index++) 512 { 513 f->key[index] = f->key[index + 1]; 514 f->ptr[index] = f->ptr[index + 1]; 515 } 516 f->keynum--; 517 } 518 else 519 { 520 T->keynum++; 521 T->key[T->keynum] = f->key[bronum]; 522 T->ptr[T->keynum] = b->ptr[0]; 523 for (index = 1;index <= b->keynum;index++) 524 { 525 T->key[index + T->keynum] = b->key[index]; 526 T->ptr[index + T->keynum] = b->ptr[index]; 527 T->keynum++; 528 } 529 free(b); 530 for (index = bronum;index <= f->keynum;index++) 531 { 532 f->key[index] = f->key[index + 1]; 533 f->ptr[index] = f->ptr[index + 1]; 534 } 535 f->keynum--; 536 } 537 renewParent(f); 538 if (f->keynum == (m + 1) / 2 - 2) 539 { 540 //調用借兄弟的函數 541 return borrowBNode(f); 542 } 543 } 544 else//如果兄弟能夠幫你 545 { 546 if (bronum < mynum) //如果我不是第一個 547 { 548 for (index = 1;index <= T->keynum;index++) 549 { 550 T->key[index + 1] = T->key[index]; 551 T->ptr[index + 1] = T->ptr[index]; 552 } 553 T->ptr[1] = T->ptr[0]; 554 T->key[1] = f->key[mynum]; 555 T->ptr[0] = b->ptr[b->keynum]; 556 T->keynum++; 557 f->key[mynum] = b->key[b->keynum]; 558 b->key[b->keynum] = 0; 559 b->ptr[b->keynum] = NULL; 560 b->keynum--; 561 562 } 563 else //如果我是第一個 564 { 565 T->keynum++; 566 T->key[T->keynum] = f->key[1]; 567 T->ptr[T->keynum] = b->ptr[0]; 568 f->key[1] = b->key[1]; 569 b->ptr[0] = b->ptr[1]; 570 for (index = 1;index <= b->keynum;index++) 571 { 572 b->key[index] = b->key[index + 1]; 573 b->ptr[index] = b->ptr[index + 1]; 574 } 575 b->keynum--; 576 } 577 } 578 return OK; 579 } 580 581 /*** 582 * @name Status deleteBTreeRecord(BTree &T, Record e) 583 * @description 實現B樹元素的刪除 584 * @return 成功返回OK,否則返回ERROR 585 * @notice 586 ***/ 587 Status deleteBTreeRecord(BTree &T, Record e) 588 { 589 BTree p, q; 590 int num, temp, index; 591 Status find_flag; 592 if (T == NULL) 593 return ERROR; 594 find_flag = findBTree(T, p, temp, e.key); 595 if (find_flag == FALSE) 596 { 597 return FALSE; 598 } 599 if (find_flag == TRUE) 600 { 601 //deleteBTreeBNode(p,temp); 602 if (p->ptr[temp] == NULL) //如果是葉子節點的話 603 { 604 for (index = temp;index <= p->keynum;++index) 605 { 606 p->key[index] = p->key[index + 1]; 607 p->ptr[index] = p->ptr[index + 1]; 608 } 609 p->keynum--; 610 if (p->keynum == (m + 1) / 2 - 2) 611 { 612 //調用借兄弟的函數 613 if (borrowBNode(p) == EMPTY) T = NULL; 614 else renewParent(T); 615 } 616 return OK; 617 } 618 else //不是葉子結點的話 619 { 620 //遍歷 621 findMax(p->ptr[temp - 1], q, num);//返回的q一定會是葉子節點 622 p->key[temp] = q->key[num]; 623 q->key[num] = 0; 624 q->keynum--; 625 if (q->keynum == (m + 1) / 2 - 2) 626 { 627 //調用借兄弟的函數 628 if (borrowBNode(q) == EMPTY) T = NULL; 629 else renewParent(T); 630 } 631 return OK; 632 } 633 return OK; 634 } 635 return ERROR; 636 } 637 /*** 638 * @name Status initBTree(BTree &t) 639 * @description 初始化一個空B樹 640 * @return 成功返回OK 641 * @notice 642 ***/ 643 Status initBTree(BTree &t) 644 { 645 t = NULL; 646 return OK; 647 } 648 /*** 649 * @name Status test() 650 * @description 針對數據結構實驗做的測試函數 651 * @return 成功返回OK 652 * @notice 653 ***/ 654 Status test() 655 { 656 // 測試代碼 657 int n, i; 658 int arr[BTREELENGTH]; 659 BTree a; 660 Record d; 661 srand((unsigned)time(NULL)); 662 n = rand() % BTREELENGTH; 663 //scanf("%d", &n); //可以改為自己輸入數據 664 printf("B樹的階為:%d,插入次數為:%d\n", m, n); 665 initBTree(a); 666 for (i = 0;i < n;i++) 667 { 668 d.key = rand() % MAXINT; 669 //scanf("%d", &d.key); //可以改為自己輸入數據 670 arr[i] = d.key; 671 if (insertBTree(a, d) == OK) 672 printf("第%d次插入%d:\n", i + 1, d.key); 673 else 674 printf("第%d次插入%d不成功:\n", i + 1, d.key); 675 print(a); 676 } 677 for (i = 0;i < n;i++) 678 { 679 d.key = arr[i]; 680 if (deleteBTreeRecord(a, d) == OK) 681 printf("第%d次刪除%d:\n", i + 1, d.key); 682 else 683 printf("第%d次刪除%d不成功:\n", i + 1, d.key); 684 print(a); 685 } 686 return OK; 687 688 } 689 /*** 690 主函數 691 ***/ 692 int main() 693 { 694 test(); 695 return 0; 696 }