b-树(c语言)

/*
	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-树的基本操作能够了解一些了,不过代码中一定还有一些细节错误



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值