b-树(c语言)

来源:互联网 发布:唯品会下单软件 编辑:程序博客网 时间:2024/05/22 00:14
/*B树的提出目的是减少读入,写入磁盘的次数个人觉得有点像是二叉树的升级并且运用到了底层的一个实例 一个2结点要么没有孩子,要有就有两个不能只有一个 */#include<stdio.h>#include<stdlib.h>#define m 3//代表节点数,也就是介数 ,m叉树 #define OK  1#define FALSE 0typedef struct BiTr{int keynum;//记录现在节点中关键字数目 int key[m+1];//关键字向量,0单元未用 .还有一个位置是用来存储分裂前多的那个关键字 struct BiTr *parent;struct BiTr *ptr[m+2];//子树指针向量 }*BiTree,bitree; typedef struct {BiTree pt;int i;//l...到m在节点中关键字序号 int tag;//1:查找成功  2:查找失败 }Result;/*为简单起见,以上说明省略了辅助信息域。在实用中,与每个关键字存储在一起的不是相关的辅助信息域,而是一个指向另一磁盘页的指针。磁盘页中包含有该关键字所代表的记录,而相关的辅助信息正是存储在此记录中。     */ //--------------------------------B树----------------------------------------------- //插入中最烦的是ptr[]的改变,到底改变到哪里去了,这个时候应该画一个简易的出来/*插入开始时就直接将k插入本该插入的位置即可,如果插入后这个节点大于本该拥有的最大节点数时就进行分裂,分裂 即将(T分成1...s,s+2 ... m,s+1三部分s+1上移,1...s移到s+1的左边,s+2...m移到s+1的右边  ) */ //------------------------------插入一个新节点-------------------------------------- void initNode(BiTree &node){node = (BiTree)malloc(sizeof(bitree));for(int i=0;i<=m+1;i++){node->ptr[i] = NULL;node->parent = NULL;}node->keynum = 0;node->parent=NULL;}int print(BiTree p){if(p==NULL)return 0; BiTree q;printf("[");for(int i = 1;i <= p->keynum;i++){printf("%d ",p->key[i]);}//printf("%d ",p->key[p->keynum+1]); printf("]");for(int i = 1;i <= p->keynum+1;i++){ //printf(" %d ",i);if(p->ptr[i]!=NULL)  {q = p->ptr[i];print(q);  }} }int Search(BiTree &T,int k){int i,min=1,max=T->keynum;//printf("%d",T->keynum);while(max >= min){int mid = (max+min)/2;//printf(" =%d %d= ",T->key[mid],k);if(T->key[mid] < k){min = mid + 1;}else if(T->key[mid] > k){max = mid -1;}elsereturn mid;//printf("gfh");} return max+1;}void Insert(BiTree &p ,int k,int n){//将k插入到key[n]中 for(int i = p->keynum;i >= n ;i-- ){p->key[i+1] = p->key[i];//key的位置一边那么他们身边的ptr也应该换位置,否则的话大小就不对了p->ptr[i+1] = p->ptr[i];}p->key[n] = k;p->ptr[n] = NULL;p->keynum++;p->ptr[n] =NULL;}Result SearchBTree(BiTree &T,int k){/* 在m介b-树T上查找关键字K,返回结果(pt,i,tag) 若成功 tag = 1,指针pt所指节点中的第i个关键字等于k  若失败,tag=0,指针pt所指节点中的第i和第i+1个关键字z之间 */ BiTree p = T;BiTree q = NULL;//q记录盘前面的那个关键字,如果不成功返回p int  Found = FALSE,i;while(p!=NULL && Found!=OK ){i = Search(p,k);/*查找该节点中的所有关键字,找到key所在的位置或者范围 */ //printf("=======\n");if(i>0 &&i<=p->keynum &&p->key[i] == k)  {  Found = OK; //printf("you%d",i);  }else{q = p;p = p->ptr[i];}}if(Found==OK){Result re;re.i = i;re.pt = p;re.tag = 1;//printf("you%d",p->key[i]);return re;}else{Result re;re.i = i;re.pt = q;re.tag = 0;return re;}//返回需要插入的位置,和二叉搜索树有点像 } //找出他是父亲的第几个孩子  int Wichson(BiTree p,int k) { BiTree T = p->parent; int i,min=1,max=T->keynum;while(max >= min){int mid = (max+min)/2;if(T->key[mid] < k){min = mid + 1;}else if(T->key[mid] > k){max = mid -1;}elsereturn mid;} return max+1; }//找到分裂后孩子们的父亲 int RenewParent(BiTree &p){//printf("FDg");if(p==NULL)return 0;for(int i = 1;i <= p->keynum+1;i++){if(p->ptr[i]!=NULL){p->ptr[i]->parent = p;BiTree q = p->ptr[i];RenewParent(q);}}}int split(BiTree &T,int s){/*将T分成1...s,s+2 ... m,s+1三部分s+1上移,1...s移到s+1的左边,s+2...m移到s+1的右边 */if(T->parent==NULL){printf("单树开始分裂\n"); BiTree node1,node2;initNode(node1);initNode(node2);for(int i=1;i<=s;i++)//初始化node1 {node1->parent = T;node1->keynum++;node1->key[i]  = T->key[i];node1->ptr[i] = T->ptr[i]; }node1->ptr[s+1] = T->ptr[s+1];//printf("DFg");int k=1;for(int i=s+2;i <= T->keynum;i++)//初始化node2{node2->parent = T;node2->keynum++;node2->key[k]  = T->key[i];node2->ptr[k] = T->ptr[i]; k++;}node2->ptr[k] = T->ptr[T->keynum+1];T->ptr[1] = node1;T->ptr[2] = node2;T->key[1] = T->key[s+1];//printf("%d",T->key[1]);T->keynum=1;//printf("DFg");return OK;}BiTree node1,node2;initNode(node1);initNode(node2);int k = T->key[s+1];//printf("key[s+1]:%d ",k);//printf("\n%d\n",k);for(int i=1;i<=s;i++)//初始化node1 {node1->parent = T;node1->keynum++;node1->key[i]  = T->key[i];node1->ptr[i] = T->ptr[i]; }int t=1;for(int i=s+2;i <= T->keynum;i++)//初始化node2{node2->parent = T;node2->keynum++;node2->key[t]  = T->key[i];node2->ptr[t] = T->ptr[i]; t++;}node2->ptr[t] = T->ptr[T->keynum+1];int index =  Wichson(T,k);//printf("---%d--%d---",index,T->parent->keynum);BiTree mm = T->parent;T->parent->ptr[T->parent->keynum +2] = T->parent->ptr[T->parent->keynum +1];//给要成为父亲的儿子腾出位置 for(int i = T->parent->keynum ; i>=index; i--){T->parent->key[i+1] = T->parent->key[i];T->parent->ptr[i+1] = T->parent->ptr[i];}T->parent->key[index] = k;T->parent->ptr[index] = node1;T->parent->ptr[index+1] = node2; //printf("%d",k); T->parent->keynum++;//T = T->parent;return OK;}int  InsertTree(BiTree &T,int k) {/*在m介b-树t上的节点*p的key[i]与k[i+1]之间插入关键字k 若引起结点过大,则沿双亲链进行必要的节点分裂调整,使T仍是M阶b-树 *///printf("Gh");BiTree p;if(T==NULL){initNode(T);T->keynum++;T->key[1] = k;//printf("%d",T->keynum);//T->ptr[1] return OK; }Result re =  SearchBTree(T,k);p = re.pt;int s = p->keynum/2; /*if(p->parent!=NULL)for(int j = 1;j<=p->parent->keynum;j++){printf("=========%d %d ==============",p->parent->keynum,p->parent->key[j]);}*///printf("\n-------------\n");//printf("%d %d",p->keynum,p->key[p->keynum/2+1]);int finished = 0; if(re.tag==1)return FALSE;Insert(re.pt,k,re.i);//printf("%d",re.pt->keynum);while(p!=NULL && finished==0){/*if(T->ptr[1]!=NULL && T->ptr[1]->ptr[2]!=NULL){printf("/////////////%d//////////",T->ptr[1]->ptr[2]->key[1]);}*/if(p->keynum < m)finished=1;else{int s = p->keynum/2;//for(int j = 1;j<=p->keynum;j++)//{//printf("****%d %d ***",p->keynum,p->key[j]);//}split(p,s);printf("||");//if(T->ptr[2]!=NULL )//{//printf("sdd://///////////%d//////////",T->ptr[2]->key[1]);//} //if(T->ptr[1]!=NULL && T->ptr[1]->ptr[2]!=NULL)//{//printf("/////////////%d//////////",T->ptr[1]->ptr[2]->key[1]);//}p = p->parent;   //if(p!=NULL) //printf(" ()%d() ",p->keynum);}RenewParent(T);}RenewParent(T);} void testInsert(){BiTree T=NULL;//initNode(T); InsertTree(T,1);//printf("%d",T->keynum);InsertTree(T,2);//printf("%d",T->keynum);InsertTree(T,3);//printf("%d ",T->keynum);InsertTree(T,4);InsertTree(T,5);print(T);InsertTree(T,7);InsertTree(T,8);InsertTree(T,9);//InsertTree(T,8);//InsertTree(T,9);//print(T);}/*------------------------------------------------------------------------------------- 删除节点是不应该考虑一下怎么旋转,用合并的思想会简单一些 不过在删除时应该考虑一下删除的结点,到底是叶子结点还是非叶子结点 同插入一般,删除就必须考虑一个叫做合并的东西  如果兄弟有多个元素那就叫兄弟给个关键字给爸爸,叫爸爸给个元素给我 (使我们还是一个b-树,没有发生变化)  如果兄弟也只有一个元素的话,这个时候就找父亲要,如果父亲被要完了   再把父亲当成我进入函数  利用循环,让合并函数循环起来 ----------------------------------删除一个节点-------------------------------*/ int mergeTree(BiTree &T,BiTree &p,int k){BiTree f = p->parent;int bronum ;int mynum = Wichson(p,k);//该节点的位置 printf("****mynum:%d******",mynum);if(mynum == 1)   bronum = 2;else bronum = mynum-1;//兄弟节点的位置//找+1,-1都可以。+1要考虑最后一个,-1要考虑第一个 BiTree b = f->ptr[bronum]; //如果节点为空,即删除的这个点为树的根节点  if(f ==NULL) { f = f->ptr[1]; if(f==NULL) return FALSE;for(int i = 0;i<= f->keynum+1;i++){p->key[i] = f->key[i];p->ptr[i] = f->ptr[i]; }p->keynum = f->keynum;RenewParent(T);return OK;  }//如果兄弟身上不只有一个关键字 if(f->ptr[bronum]->keynum > (m/2)){if(bronum < mynum)//如果p不是父亲的第一个儿子 {p->keynum++;p->ptr[p->keynum+2]= p->ptr[p->keynum+1];for(int j = 1; j <= p->keynum;j++){p->key[j+1] = p->key[j];p->ptr[j+1]= p->ptr[j];} p->key[1] = f->key[mynum-1];//因为父亲一定比我的值小所以将其赋到第一个p->ptr[1] = b->ptr[b->keynum+1];f->key[mynum-1] = b->key[b->keynum];b->keynum--;  }else{p->keynum++;//为了得到父亲的一个关键字,所以必须腾出位置 for(int j = 1; j <= p->keynum;j++){p->key[p->keynum+2] = p->key[p->keynum+1];p->key[j+1] = p->key[j];p->ptr[j+1]= p->ptr[j];} p->key[1] = f->key[mynum];//因为父亲一定我的值小所以将其赋到第一个p->ptr[1] = b->ptr[1];f->key[mynum] = b->key[b->keynum];b->keynum--; }}//如果兄弟身上也只有一个关键字时,就让我和他以及父亲指向我的节点合并在一起 else{if(bronum < mynum)//如果p不是父亲的第一个儿子 {b->keynum++;b->key[b->keynum] = f->key[bronum];b->ptr[b->keynum] = NULL;for(int j=1;j<=p->keynum;j++){b->key[b->keynum+j] = p->key[j];b->ptr[b->keynum+j] = p->ptr[j];}for(int j=1;j <= p->keynum;j++){b->keynum++;}k = f->key[bronum];for(int j = bronum ; j <= f->keynum ;j++ ){f->key[j] = f->key[j+1];f->ptr[j] = f->ptr[j+1];}f->keynum--;}else//同理 {for(int j=b->keynum;j>=1;j--){b->key[j+p->keynum+1] = b->key[j];b->ptr[p->keynum+1+j] = b->ptr[j];}for(int j=1;j<=p->keynum;j++){b->keynum++;}for(int j=1;j <= p->keynum+1;j++){b->key[1+j] = p->key[j];b->ptr[j+1] = p->ptr[j];}b->key[1] = p->key[mynum];b->ptr[1] = NULL;k = f->key[bronum];for(int j = bronum ; j <= f->keynum ;j++ ){f->key[j] = f->key[j+1];f->ptr[j] = f->ptr[j+1];}f->keynum--;}} RenewParent(T);if(f->keynum==0)mergeTree(T,f,k);return 0;}int FindMax(BiTree p,BiTree &t,int &max){while(p!=NULL){p =p->ptr[p->keynum];} t = p; max = p->keynum; }//当T->keynum小与m/2时则需要合并 int DeleteTree(BiTree &T,int k){BiTree p;Result re =  SearchBTree(T,k);p = re.pt;int finished = 0; if(re.tag==0)return FALSE;//Delete(re.pt,k,re.i);//printf("%d",re.pt->key[1]);//如果删除的节点为叶子节点 if(p->ptr[re.i]==NULL){printf("删除节点为:%d===",k);for(int j = re.i ;j <= p->keynum;j++ ){p->key[j] = p->key[j+1];p->ptr[j] = p->ptr[j+1];}p->keynum--;if(p->keynum==0){printf("进入合并!"); if(mergeTree(T,p,k)){//printf("FDgf");RenewParent(T);}else return FALSE;}}//如果删除的不只叶子结点else{BiTree t;int max;FindMax(p->ptr[re.i],t,max);//找到那个下的最大的结点t,然后max存储最大值的坐标 p->key[re.i] =  t->key[max];//因为最大的一定是结点的最后一个所以无需移动t->keynum--;if(t->keynum==0){if(mergeTree(T,p,k))RenewParent(T);else return FALSE;} return OK;} return OK;}void test2(){BiTree T=NULL;printf("分并插入元素点:14,13,15,9,1,2,14:"); InsertTree(T,11);InsertTree(T,14);InsertTree(T,13);InsertTree(T,15);InsertTree(T,9);InsertTree(T,1);//print(T);//printf("/////////////%d//////////",T->key[1]); InsertTree(T,2);//printf("/////////////%d//////////",T->ptr[1]->ptr[2]->keynum); //InsertTree(T,9);printf("\n生成b-树为:"); print(T);printf("\n");//printf("%d",Search(T,4));DeleteTree(T,14);printf("删除后:"); print(T);//InsertTree(T,8);//InsertTree(T,9);//print(T);}int main(){test2();return 0;}





通过1周的学习,感觉b-树的基本操作能够了解一些了,不过代码中一定还有一些细节错误



0 0
原创粉丝点击