B樹——思路、及C語言代碼的實現


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 }
BTree

 


免責聲明!

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



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