数据结构与算法分析——c语言描述 第四章树 B-树
2016-04-14创建:
好久没更新博客,这7天断断续续写B树,学汇编,学计算机组成原理。
B树好难啊,还没写完。只写了25%。。。
插入剩下两种情况没写:
1.祖父未满,父亲满,儿子满。
2.祖父满,父亲满,儿子满。
想不到怎么写。这两个情况有两种相同的地方,把父亲拆成两个。
父亲拆成两个可以这样写:像父亲未满,儿子满那样,新建一个儿子,移动儿子间的元素,插入元素,父亲拆成两个(更新儿子数量,儿子指针,更新索引)。
接下来
对于第一个情况:
告诉祖父要新建儿子(这里又要像伸展树那样用结构了,告诉祖父要增加儿子,要包括新儿子在儿子分支的左边还是右边,和新儿子的指针),祖父要更新儿子指针,更新数量,索引。
对于第二个情况:
祖父满了。拆成两个祖父,告诉曾祖父多了一个儿子。这个情况和第二个情况一样。
2016.4.15更新
这个B树8天。终于把插入写完了。写这个B树是真的真的要狗带了。写得我很烦躁,感到智商非常不够用。不吐槽了。说一说收获。
写代码是用脑子想出来的,不是一脑门光写,不断测试,不断改,不断重复循环。这个办法虽然很快来结果,但是效率很低,并且只能应付简单的编程,一遇到难的就傻了。整天用调试跟踪和printfh输出调试结果,这样效率非常低非常低。
昨天和今天我意识到这个问题。我先关了电脑。在几张纸上不断写出所有情景下插入的情况,非常详细的。然后从这么多过程中提取相同的过程,做成7,8个私有函数,这些函数(过程)都是经常需要用到的。我手写了这些函数的声明表,想该传入什么参数,返回类型。
写完后稍微改改,没怎么调试。别怕,想出来,深度思考是痛苦的,但这是人和动物的区别。
有一个地方感觉处理不是很好。因为树的结构经常更改,所以insert要像伸展树调用internalinsert,接口的insert是用于假如insert插入多了一个兄弟,然后要构建两者的父亲。我就是复制了相同的代码。更改了返回新儿子的部分。不知道有没有更优雅的代码组织方式。大神请指教!
2016.4.19更新
删除很快就写完了,和插入差不多。代码是想出来的,不是不停debug弄出来的。看着曾经的日志还写着“不停debug是一个不停的思考和创作过程”。。。确实这样,但没必要这么折腾,况且这方法对简单的编程才有效,难的话连续弄几天也弄不出来。正确的姿势是先想过程,从全局的角度想想需要定义哪些函数。
到了学期中就是事多。。。。加上自己确实是懒了。不能再这样下去。
btree.h
typedef int ElementType; #ifndef _B_Tree_h #define _B_Tree_h struct BtreeNode; typedef struct BtreeNode* PtrToNode; typedef struct BtreeNode* Btree; Btree createBtree(); void makeEmpty(Btree t); PtrToNode find(ElementType X, Btree t); Btree insert(ElementType X, Btree t); Btree Delete(ElementType X, Btree t); void Dir(Btree t); #endif
btree.c
#include"btree.h" #include<stdlib.h> #include"fatal.h" struct BtreeNode; #define M 3 struct BtreeNode { int type; int sonNum;//儿子数量,或当前拥有数据的数量(最底层) PtrToNode PtrToSon[M]; ElementType elem[M]; }; static int binarySearch(PtrToNode bottomNode, ElementType X) {//对分搜索 if (bottomNode->type != 2) Error("ERROR!"); ElementType* arr = bottomNode->elem; int n = bottomNode->sonNum; if (n > 0) { int low = 0; int high = n - 1; while (low <= high) { int mid = (high + low) / 2; if (X < arr[mid]) { high = mid - 1; } else if (X > arr[mid]) { low = mid + 1; } else return mid; } return -1; } return -1; } static int getSonBranch(PtrToNode Node, ElementType X) { ElementType* arr = Node->elem; int n = Node->sonNum; if (n == 1) return 0; else if (n == 2) { if (X < arr[0]) return 0; else return 1; } else if (n >= 2) { int low = 0; int high = n - 1 - 1;//第一个分支没有索引 while (low <= high) { int mid = (high + low) / 2; if (X < arr[mid]) { high = mid - 1; } else if (X > arr[mid]) { low = mid + 1; } else return mid + 1;//第一个分支没有索引 } return high + 1;//退出循环,此时high在左,low在右,X位于high和low的值之间 } else { Error("GET SON BRANCH ERROR"); } } static void binaryInsert(ElementType arr[], int n, ElementType X) { int low = 0; int high = n - 1; while (low <= high) { int mid = (low + high) / 2; if (arr[mid] < X) { low = mid + 1; } else if (arr[mid] > X) high = mid - 1; } while (arr[low] < X&& low < n) { low++; } for (int i = n; i > low; i--) { arr[i] = arr[i - 1]; } arr[low] = X; } static void binaryInsertForBottomNode(PtrToNode bottomNode, ElementType X) {//对分插入 if (bottomNode->type != 2) Error("eror!"); ElementType* arr = bottomNode->elem; int n = bottomNode->sonNum; if (n > 0) { binaryInsert(arr, n, X); } else arr[0] = X; bottomNode->sonNum++; } static PtrToNode allocNode(int type) {//0为内部节点 1为叶子 2为底层节点 PtrToNode p = malloc(sizeof(struct BtreeNode)); if (p == NULL) Error("OUT OF SPACE!!"); p->type = type; p->sonNum = 0; return p; } static void updateIndex(PtrToNode p) { if (p->sonNum >= 2) { for (int i = 0; i < p->sonNum - 1; i++) { PtrToNode son = p->PtrToSon[i + 1]; while (son->type != 2) { son = son->PtrToSon[0]; } p->elem[i] = son->elem[0]; } } } static PtrToNode insertAndSplitBottomNode(PtrToNode b1, ElementType X) { if (b1->sonNum != 3) Error("ERROR!"); PtrToNode b2 = allocNode(2); ElementType tempElem[4]; for (int i = 0; i < 3; i++) tempElem[i] = b1->elem[i]; binaryInsert(tempElem, 3, X); for (int i = 0; i < 2; i++) b1->elem[i] = tempElem[i]; for (int i = 0; i < 2; i++) b2->elem[i] = tempElem[i + 2]; b1->sonNum = b2->sonNum = 2; return b2; } static PtrToNode split(Btree father, PtrToNode newSon, int sonBranch) { PtrToNode allSon[4]; int i, j; for (i = 0, j = 0; j <= sonBranch; i++, j++) allSon[i] = father->PtrToSon[j]; allSon[i++] = newSon; for (; j < father->sonNum; i++, j++) allSon[i] = father->PtrToSon[j]; PtrToNode newFater = allocNode(father->type); father->sonNum = 2; father->PtrToSon[0] = allSon[0]; father->PtrToSon[1] = allSon[1]; updateIndex(father); newFater->sonNum = 2; newFater->PtrToSon[0] = allSon[2]; newFater->PtrToSon[1] = allSon[3]; updateIndex(newFater); return newFater; } static void insertPtrToSon(PtrToNode father, PtrToNode newSon, int sonBranch) { if (father->sonNum == 0) { father->PtrToSon[0] = newSon; } else { int i; for (i = father->sonNum; i > sonBranch + 1; i--) { father->PtrToSon[i] = father->PtrToSon[i - 1]; } father->PtrToSon[sonBranch + 1] = newSon; } father->sonNum++; } Btree createBtree() { Btree t = malloc(sizeof(struct BtreeNode)); if (t == NULL) Error("OUT OF MEMORY!"); t->sonNum = 0; t->type = 1;//空树,根节点也是树叶 return t; } void makeEmpty(Btree t) { if (t->type) { for (int i = 0; i < t->sonNum; i++) free(t->PtrToSon[i]); free(t); } else { for (int i = 0; i < t->sonNum; i++) { makeEmpty(t->PtrToSon[i]); } free(t); } } PtrToNode find(ElementType X, Btree t) { if (t->type == 0) {//内部节点 int p = getSonBranch(t, X); return find(X, t->PtrToSon[p]); } else { int p; if (t->sonNum == 0)//刚创建树的时候,空 return NULL; else if (t->sonNum == 1) { p = binarySearch(t->PtrToSon[0], X); if (p == -1) return NULL; else return t->PtrToSon[0]; } else { p = getSonBranch(t, X);//选择儿子分支 int tempCursor = binarySearch(t->PtrToSon[p], X);//在最底层中查找 if (tempCursor == -1) return NULL; return t->PtrToSon[p]; } } } static void insertElem_LeafEmpty(ElementType X, Btree t) { PtrToNode newSon = allocNode(2); binaryInsertForBottomNode(newSon, X); insertPtrToSon(t, newSon, 0); } static void insertElem_LeafSonNotFull(ElementType X, Btree t, int sonChoice) { binaryInsertForBottomNode(t->PtrToSon[sonChoice], X); updateIndex(t); } static Btree insert_internal(ElementType X, Btree t); static void insertElem_LeafNotFull_LeafSonFull(ElementType X, Btree t, int sonBranch) { PtrToNode newSon = insertAndSplitBottomNode(t->PtrToSon[sonBranch], X); insertPtrToSon(t, newSon, sonBranch); updateIndex(t); } static PtrToNode insertElem_LeafFull_LeafSonFull(ElementType X, Btree t, int sonBranch) { PtrToNode newSon = insertAndSplitBottomNode(t->PtrToSon[sonBranch], X); PtrToNode newFather = split(t, newSon, sonBranch); return newFather; } static Btree insert_internal(ElementType X, Btree t) { if (t->type == 0) {//非叶子 int sonBranch = getSonBranch(t, X); PtrToNode newSon = insert_internal(X, t->PtrToSon[sonBranch]); if (newSon) { if (t->sonNum < M) { insertPtrToSon(t, newSon, sonBranch); updateIndex(t); return NULL; } else { PtrToNode newfather = split(t, newSon, sonBranch); return newfather; } } else { return NULL; } } else if (t->type == 1) {//叶子 if (t->sonNum == 0) { insertElem_LeafEmpty(X, t); return NULL; } else { int sonBranch = getSonBranch(t, X);//选择儿子分支 int XCursor = binarySearch(t->PtrToSon[sonBranch], X);//在儿子中查找 if (XCursor == -1) {//不存在X if (t->PtrToSon[sonBranch]->sonNum < M) {//叶子的儿子数量未满 insertElem_LeafSonNotFull(X, t, sonBranch); return NULL; } else if (t->sonNum < M && t->PtrToSon[sonBranch]->sonNum == M) {//父亲未满,儿子满 insertElem_LeafNotFull_LeafSonFull(X, t, sonBranch); return NULL; } else {//父亲满,儿子满 PtrToNode newfather = insertElem_LeafFull_LeafSonFull(X, t, sonBranch); return newfather; } } else return NULL;//已存在X } } else { Error("type error"); } } Btree insert(ElementType X, Btree t) { if (t->type == 0) {//非叶子 int sonBranch = getSonBranch(t, X); PtrToNode newSon = insert_internal(X, t->PtrToSon[sonBranch]); if (newSon) { if (t->sonNum < M) { insertPtrToSon(t, newSon, sonBranch); updateIndex(t); return t; } else { PtrToNode newfather = split(t, newSon, sonBranch); PtrToNode p = allocNode(0); insertPtrToSon(p, t, 0); insertPtrToSon(p, newfather, 0); updateIndex(p); return p; } } else { return t; } } else if (t->type == 1) {//叶子 if (t->sonNum == 0) { insertElem_LeafEmpty(X, t); return t; } else { int sonBranch = getSonBranch(t, X);//选择儿子分支 int XCursor = binarySearch(t->PtrToSon[sonBranch], X);//在儿子中查找 if (XCursor == -1) {//不存在X if (t->PtrToSon[sonBranch]->sonNum < M) {//叶子的儿子数量未满 insertElem_LeafSonNotFull(X, t, sonBranch); return t; } else if (t->sonNum < M && t->PtrToSon[sonBranch]->sonNum == M) {//父亲未满,儿子满 insertElem_LeafNotFull_LeafSonFull(X, t, sonBranch); return t; } else {//父亲满,儿子满 PtrToNode newfather = insertElem_LeafFull_LeafSonFull(X, t, sonBranch); PtrToNode p = allocNode(0); insertPtrToSon(p, t, 0); insertPtrToSon(p, newfather, 0); updateIndex(p); return p; } } else return t;//已存在X } } else Error("type error!"); } void Dir(Btree t) { if (t->type == 0) { printf("\n"); for (int i = 0; i < t->sonNum; i++) { Dir(t->PtrToSon[i]); } } else if (t->type == 1) { for (int i = 0; i < t->sonNum; i++) { if(i==1||i==2) printf("%d: ", t->elem[i-1]); for (int j = 0; j < t->PtrToSon[i]->sonNum; j++) { printf("%d ", t->PtrToSon[i]->elem[j]); } printf(" "); } printf("\n"); } } static void deleteAndFreePtrToSon(PtrToNode father, int sonBranch) { free(father->PtrToSon[sonBranch]); for (int i = sonBranch; i < father->sonNum - 1; i++) { father->PtrToSon[i] = father->PtrToSon[i + 1]; } father->sonNum--; } static void deletePtrToSon(PtrToNode father, int sonBranch) { for (int i = sonBranch; i < father->sonNum - 1; i++) { father->PtrToSon[i] = father->PtrToSon[i + 1]; } father->sonNum--; } static void binaryDeleteForBottomNode(PtrToNode bottomNode, ElementType X) { int p = binarySearch(bottomNode, X); if (p != -1) { for (int i = p; i < bottomNode->sonNum - 1; i++) { bottomNode->elem[i] = bottomNode->elem[i + 1]; } bottomNode->sonNum--; } } static int delete_internal(ElementType X, Btree t) {//返回类型0表示儿子够,1表示儿子不够 if (t->type == 0) {//内部节点 int p = getSonBranch(t, X); int isSon_NotEnough_grandson = delete_internal(X, t->PtrToSon[p]); if (isSon_NotEnough_grandson == 0) return 0; else {//// int brother; if (p + 1 < t->sonNum) { brother = p + 1; } else { brother = p - 1; } if (t->PtrToSon[brother]->sonNum == 2) { if (p < brother) { insertPtrToSon(t->PtrToSon[brother], t->PtrToSon[p]->PtrToSon[0], -1); } else insertPtrToSon(t->PtrToSon[brother], t->PtrToSon[p]->PtrToSon[0], 1); updateIndex(t->PtrToSon[brother]);//这个顺序一定要在前面,因为deleteAndFreePtrToSon移动了儿子指针,所以要先更新 deleteAndFreePtrToSon(t, p); updateIndex(t); return t->sonNum == 1; } else if (t->PtrToSon[brother]->sonNum == 3) { if (p < brother) { insertPtrToSon(t->PtrToSon[p], t->PtrToSon[brother]->PtrToSon[0], 0); deletePtrToSon(t->PtrToSon[brother], 0); } else { insertPtrToSon(t->PtrToSon[p], t->PtrToSon[brother]->PtrToSon[2],-1); deletePtrToSon(t->PtrToSon[brother], 2); } updateIndex(t->PtrToSon[p]); updateIndex(t->PtrToSon[brother]); updateIndex(t); return 0; } else { Error("what the hell?"); return 0; } } } else if (t->type == 1) {//叶子 int p; if (t->sonNum == 0)//刚创建树的时候,空 return 0; if (t->sonNum == 1) { p = 0; } else { p = getSonBranch(t, X);//选择儿子分支 } binaryDeleteForBottomNode(t->PtrToSon[p], X); if (t->PtrToSon[p]->sonNum == 0) { deleteAndFreePtrToSon(t, p); return 0; } else if (t->PtrToSon[p]->sonNum == 1) { int brother; if (p > 0) { brother = p - 1; } else if (p + 1 < t->sonNum) { brother = p + 1; } else {//只有一个儿子,无兄弟 return 0; } if (t->PtrToSon[brother]->sonNum == 3) { if (brother < p) { t->PtrToSon[p]->elem[1] = t->PtrToSon[p]->elem[0]; t->PtrToSon[p]->elem[0] = t->PtrToSon[brother]->elem[2]; t->PtrToSon[p]->sonNum++; t->PtrToSon[brother]->sonNum--; } else { t->PtrToSon[p]->elem[1] = t->PtrToSon[brother]->elem[0]; for (int i = 0; i < 2; i++) { t->PtrToSon[brother]->elem[i] = t->PtrToSon[brother]->elem[i + 1]; } t->PtrToSon[p]->sonNum++; t->PtrToSon[brother]->sonNum--; } updateIndex(t); return 0; } else if (t->PtrToSon[brother]->sonNum == 2) { binaryInsertForBottomNode(t->PtrToSon[brother], t->PtrToSon[p]->elem[0]); deleteAndFreePtrToSon(t, p); updateIndex(t); return t->sonNum == 1; } else { Error("what the hell?"); return 0; } } else//底层节点关键字数目为2 return 0; } else { Error("what the hell?"); return 0; } } Btree Delete(ElementType X, Btree t) { if (delete_internal(X, t)) { if (t->type == 0) { Btree newRoot = t->PtrToSon[0]; free(t); return newRoot; } } updateIndex(t); return t; }
main.c
#include"btree.h" #include<stdio.h> #include<stdlib.h> #define M 3 struct BtreeNode { int type; int sonNum;//儿子数量,或当前拥有数据的数量(最底层) PtrToNode PtrToSon[M]; ElementType elem[M]; }; int main() { Btree t = createBtree(); for (int i = 0; i < 25; i++) t = insert(i, t); Dir(t); printf("\n"); for (int i = 0; i < 12; i++) t = Delete(i, t); Dir(t); }