/*
B树的提出目的是减少读入,写入磁盘的次数
个人觉得有点像是二叉树的升级并且运用到了底层的一个实例
一个2结点要么没有孩子,要有就有两个不能只有一个
*/
#include<stdio.h>
#include<stdlib.h>
#define m 3//代表节点数,也就是介数 ,m叉树
#define OK 1
#define FALSE 0
typedef 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;
}
else
return 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;
}
else
return 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-树的基本操作能够了解一些了,不过代码中一定还有一些细节错误