B+树的实现

 

             B+树是B树的变形,它在B树的节点里删除了关键字的指针域,只保留了链接节点的指针域,在叶节点上,这个链接节点的指针域用来保存关键字信息。B+树中的关键字在树中可能不止出现一次(最多出现两次),但一定在叶节点出现一次。相邻的叶节点用单链表形式连接起来,也就是说,找到了最左的叶节点后,所有关键字信息就可以按照遍历单链表的形式遍历出来。

 

             虽然在B+树中关键字可能会冗余存储(最多有两个相同的关键字,分别在内节点和叶节点),而且查询时必须查到叶节点才算结束,但相对于B树来说,B+树删除了一个关键字的指针域,因此节点占用空间更小,消耗I/O的时间更小,从而平均效率要高于B树。由于所有关键字在叶节点上都有保存,因此B+树还能支持复杂度为O(logn)的范围查询,这点B树是不能做到的。

 

B+树的插入过程

 

          B+树的插入过程与B树相似,都是插到叶节点内。只是在对叶节点分裂时稍有不同,B树分裂叶节点的过程是:将该节点的中间关键字上移至其父亲节点,剩下前半部分的关键字组成新的左叶节点,后半部分是令一个右叶节点,上移的关键字不在叶节点出现了,而B+树分裂叶节点的过程是在上移中间关键字后,还在左叶节点保存这个关键字。

 

 

B+树的查询过程

          B+树的查询与B树相似,但是由于B+树只有在叶节点时指针域才保存该关键字的信息,所以必须查询到叶节点才算结束,而B树没有这个限制,和B树相比,B+树的查询比较稳定。

 

B+树的删除过程

          在B+树中删除关键字是操作B+树中最为复杂的部分,与B树的删除过程相比,B+树不仅要将内节点的关键字删除,而且必须把叶节点的关键字也删除,因此显得更为繁琐。B+树的删除有很多不同的情况,以下列出了具体每种情况和解决过程,并且每种情况的操作过程都不违背B+树的性质。为了保证删除过程没有回溯,因此在递归删除孩子节点的关键字时,需要保证孩子节点的关键字超过半满

 

If(在当前节点找到关键字)

{

           If(当前节点是内节点)

           {

                     If(当前找到的关键字的孩子是叶节点)

                     {

                              If(关键字的孩子节点达到半满)

                              {

                                       情况A

                              }

                              Else(关键字的孩子不到半满)

                              {

                                        If(关键字有左兄弟)

                                        {

                                                   If(左兄弟的孩子达到半满)

                                                   {

                                                              情况B

                                                   }

                                                   Else(左兄弟的孩子不到半满)

                                                   {

                                                               情况C

                                                   }

                                       }

                                       Else(关键字只有右兄弟)

                                        {

                                                  If(右兄弟的孩子达到半满)

                                                  {

                                                              情况D

                                                  }

                                                  Else(右兄弟的孩子不到半满)

                                                  {

                                                             情况E

                                                  }

                                        }

                              }

                    }

                    Else(当前关键字的孩子是内节点)

                    {

                              情况F

                    }

          }

          Else(当前节点是叶节点)

          {

                    情况G

          }

}

Else(当前节点没找到关键字)

{

          If(当前节点是内节点)

          {

                    If(如果关键字的孩子是内节点)

                    {

                              If(关键字的孩子达到半满)

                              {

                                        情况H

                              }

                              Else(关键字的孩子不到半满)

                              {

                                        If(关键字有左兄弟)

                                        {

                                                  If(关键字左兄弟的孩子达到半满)

                                                  {

                                                            情况I

                                                  }

                                                  Else(关键字左兄弟的孩子不到半满)

                                                  {

                                                            情况J

                                                  }

                                        }

                                        Else(关键字只有右兄弟)

                                        {

                                                  If(关键字右兄弟的孩子达到半满)

                                                  {

                                                            情况K

                                                  }

                                                  Else(关键字右兄弟的孩子不到半满)

                                                  {

                                                            情况L

                                                  }

                                        }

                              }

                    }

                    Else(关键字的孩子是叶节点)

                    {

                              If(关键字的孩子达到半满)

                              {

                                        情况M

                              }

                              Else(关键字的孩子不到半满)

                              {

                                        If(关键字有左兄弟节点)

                                        {

                                                  If(关键字的左兄弟节点的孩子达到半满)

                                                  {

                                                            情况N

                                                  }

                                                  Else(关键字的左兄弟节点的孩子没到半满)

                                                  {

                                                            情况O

                                                  }

                                        }

                                        Else(关键字只有右兄弟节点)

                                        {

                                                  If(关键字的右兄弟节点的孩子达到半满)

                                                  {

                                                            情况P

                                                  }

                                                  Else(关键字的右兄弟节点的孩子没到半满)

                                                  {

                                                            情况Q

                                                  }

                                        }

                              }

                    }

          }

}

 

 

情况A

          由于在内节点找到了要删除的关键字,并且该关键字的孩子是叶节点,由B+树每个关键字都必须在叶节点出现的性质可知:x.key[i]必然等于其叶节点的孩子的最后一个关键字child.key[n-1]。此时用孩子节点的倒数第二个关键字替换x.key[i],再递归删除孩子节点中的关键字即可。

 

情况B

          当发生情况B时,由于lbchild和child都是叶节点,因此必然有x.key[i-1]等于lbchild.key[n-1],key[i]等于child.key[0],由于关键字key[i]的左兄弟的孩子lbchild达到半满,因此可以将左兄弟关键字x.key[i-1]插入到孩子节点child的头部,用左兄弟孩子的倒数第二个关键字lbchild.key[n-2]替换左兄弟x.key[i-1]后,并将lbchild.key[n-1]在lbchild中删掉。最后递归删除child中的关键字。

 

情况C :         

          此时关键字的孩子节点和左兄弟孩子都没达到半满,此时需要将child合并到lbchild节点后部,释放孩子节点child所占用的空间,最后在x节点中删除左兄弟key[i-1]关键字,其后的关键字前移一位,最后递归删除lbchild中的关键字。

 

情况D:

          此种情况下,关键字只有右兄弟,并且右兄弟的孩子节点达到半满。这时可以用右兄弟的孩子节点中的最小关键字rbchild.key[0]替代x.key[i],并将rbchild.key[0]插入到child的末尾,再在richild中删除rbchild.key[0],最后递归删除child中的关键字。

 

情况E:

          如果右兄弟的孩子节点不到半满,此时需要将右兄弟的孩子节rbchild点合并到child的末尾,过程和情况C类似,只不过顺序调换一下。

 

情况F:

          此时在内节点找到要删除的关键字,并且该关键字的孩子节点也是内节点,这时可以找到该关键字在B+树叶节点的左兄弟关键字,用这个关键字替代x.key[i],然后递归删除x.key[i]孩子中的关键字,递归删除之前还需要保证孩子节点达到半满。

 

情况G:

          此时在叶节点找到关键字,直接将其在该叶节点删除即可。

 

情况H:

          直接递归删除以x.key[i]为根的子树中的关键字即可。

 

情况I:

          此时x.key[i]的孩子child不到半满,但是其左兄弟x.key[i-1]的孩子lbchild达到半满,因此可以将左兄弟关键字x.key[i-1]移至其孩子节点child的首部,child的其他关键字和指针向后移一位,将左兄弟孩子节点lbchild的最后一个关键字lbchild.key[n-1]移至key[i-1],再将lbchild的最后一个指针域复制到child的第一个指针域,然后递归删除x.key[i]子树中的关键字即可。上述过程结束后,lbchild减少了一个关键字,child增加了一个关键字。

 

情况J:

          如果x.key[i]的孩子child不到半满,并且其左兄弟关键字的孩子也不到半满,则将左兄弟关键字x.key[i-1]下移至其孩子lbchild的末尾,然后再将child节点拷贝到lbchild的末尾,这个过程结束后再将x.key[i-1]之后的关键字和指针前移一位,再递归删除x.key[i-1]孩子中的关键字即可。上述过程结束后,当前节点x减少一个关键字,child节点被删除。

 

情况K:

          此时x.key[i]没有左兄弟,只有右兄弟,并且右兄弟关键字x.key[i+1]的孩子rbchild达到半满。这时将x.key[i]下移至child节点末尾,将rbchild的首指针移至child节点末尾,再将rbchild的首关键字拷贝到x.key[i],rbchild中关键字和指针前移一位后即调整完毕,然后递归删除x.key[i]孩子中的关键字即可。上述操作后,child增加了一个关键字,rbchild减少了一个关键字。

 

情况L:

          此时x.key[i]没有左兄弟,只有右兄弟,但是右兄弟的孩子rbchild没到半满,这就需要将rbchild合并到child节点中,具体过程是:先将x.key[i]下移至child末尾,再将rbchild中的关键字和指针依次拷贝到child的末尾,然后将x,key[i]以后的关键字和指针前移一位即可。上述过程结束后,当前节点x减少一个关键字,rbchild节点被删除。

 

情况M:

          直接递归删除child中的关键字即可。

 

情况N:

          此时关键字有左兄弟,并且左兄弟节点的孩子lbchild达到半满。此时将lbchild的最后一个关键字和指针移至child的首部,child的其他关键字和指针后移一位,再将lbchild.key[n-2]复制到左兄弟x.key[i-1]即可。上述过程结束后,lbchild减少一个关键字,child增加一个关键字。

 

情况O :        

          此时关键字有左兄弟,并且左兄弟的孩子lbchild没到半满,则需要将child与lbchild合并,合并的过程是:直接将child中的关键字和指针依次拷贝至lbchild的末尾,然后将x.key[i-1]后的关键字前移一位即可。

 

情况P:

          此时关键字只有右兄弟,并且右兄弟的孩子达到半满。这时将rbchild的第一个关键字和其指针拷贝至child末尾,并且将x.key[i]替换为rbchild的第一个关键字,最后rbchild中的关键字和指针前移一位。上述操作结束后,rbchild中减少一个关键字,child增加一个关键字。

 

情况Q:

          此时关键字只有右兄弟,并且右兄弟的孩子没到半满,这是需要将child和rbchild合并,合并的过程是:直接将rbchild中的关键字和指针依次拷贝至child末尾,然后x.key[i]后的关键字前移一位即可。该过程结束后,x中减少一个关键字,rbchild被删除。

 

 

 

/*
	运行前需在程序目录建立名为Bfile的文件,否则崩溃
*/

#include<iostream>
#include<time.h>
using namespace std;


#define MAX_KEY 5	//B+树的阶,必须为大于3奇数

typedef	 __int64 KEYTYPE;
typedef	 unsigned long	FILEP;

//B+树节点的数据结构
typedef struct
{
	  KEYTYPE	key[MAX_KEY] ;		//关键字域
	  FILEP	Pointer[MAX_KEY+1] ;	//指针域
	  int		nkey ;				//关键字数
	  bool	isleaf ;				//是否为叶节点 叶节点:true 否则为false
	  
}BPlusNode;


//插入关键字的数据结构
typedef struct
{
	  KEYTYPE	key;			  //该记录的关键字
	  FILEP	Raddress;		  //该关键字对应记录的地址
	  
}TRecord;


//保存查询结果的数据结构
typedef struct			
{
	  bool	exist;
	  FILEP	Baddress;	//保存包含该记录的B+树节点地址
	  FILEP	Raddress;	//该关键字的所指向的记录地址
	  
}SearchResult;



class BPlusTree
{
	  FILEP ROOT;		//树根在文件内的偏移地址
	  FILE	*Bfile;		//B+树文件的指针
	  FILE	*Rfile;		//记录文件的指针
	  
public:
	  
	  FILEP	GetBPlusNode() const;
	  void	ReadBPlusNode(const FILEP ,BPlusNode& ) const;
	  void	WriteBPlusNode(const FILEP ,const BPlusNode& );
	  
	  void	Build_BPlus_Tree();
	  
	  void	Insert_BPlus_Tree(TRecord& );
	  void	insert_bplus_tree(FILEP ,TRecord& );
	  
	  void	Split_BPlus_Node(BPlusNode& ,BPlusNode& ,const int );
	  
	  void	Search_BPlus_Tree(TRecord& ,SearchResult& ) const;
	  
	  void	Delete_BPlus_Tree(TRecord& );
	  void	delete_BPlus_tree(FILEP ,TRecord& );
	  
	  void  EnumLeafKey();
	  
	  
	  BPlusTree();
	  ~BPlusTree();
	  
};



BPlusTree :: BPlusTree()
{
	  Bfile = fopen("Bfile" ,"rb+" );	  //打开B+树文件
	  
}

BPlusTree :: ~BPlusTree()
{
	  fclose(Bfile );
}



void	BPlusTree :: Build_BPlus_Tree()	  //建立一棵空B+树
{
	  ROOT = GetBPlusNode();
	  BPlusNode r;
	  r.Pointer[MAX_KEY] = 0 ;
	  r.nkey = 0;
	  r.isleaf = true ;
	  WriteBPlusNode(ROOT ,r );
}



void	BPlusTree :: Insert_BPlus_Tree(TRecord &record )		//向B+树插入关键字
{
	  BPlusNode r;
	  ReadBPlusNode(ROOT ,r );
	  
	  if( r.nkey == MAX_KEY )
	  {
			BPlusNode newroot ;
			newroot.nkey = 0;
			newroot.isleaf = false;
			newroot.Pointer[0] = ROOT ;
			
			Split_BPlus_Node(newroot ,r ,0 );
			WriteBPlusNode(ROOT ,r );
			
			ROOT = GetBPlusNode();
			
			WriteBPlusNode(ROOT ,newroot );
			
			//分裂根节点
	  }
	  insert_bplus_tree(ROOT ,record );
}



void	BPlusTree :: insert_bplus_tree(FILEP current ,TRecord &record )
{
	  BPlusNode x ;
	  ReadBPlusNode(current ,x );
	  
	  int	i;
	  for(i = 0 ; i < x.nkey && x.key[i] < record.key ; i ++);
	  
	  if(i < x.nkey && x.isleaf && x.key[i] == record.key )	//在B+树叶节点找到了相同关键字
	  {
			//关键字插入重复
			return ;
	  }
	  
	  if(!x.isleaf )	//如果不是叶节点
	  {
			BPlusNode y;
			ReadBPlusNode(x.Pointer[i] ,y );
			
			if( y.nkey == MAX_KEY )		//如果x的子节点已满,则这个子节点分裂
			{
				  Split_BPlus_Node(x ,y ,i );
				  WriteBPlusNode(current ,x );
				  WriteBPlusNode(x.Pointer[i] ,y );
			}
			if( record.key <= x.key[i] || i == x.nkey )
			{
				  insert_bplus_tree(x.Pointer[i] ,record );
			}
			else
			{
				  insert_bplus_tree(x.Pointer[i+1] ,record );
			}
			
	  }
	  else			//如果是叶节点,则直接将关键字插入key数组中
	  {
			
			for(int j = x.nkey ; j > i ; j--)
			{
				  x.key[j] = x.key[j-1] ;
				  x.Pointer[j] = x.Pointer[j-1] ;
			}
			x.key[i] = record.key ;
			x.nkey ++;
			
			//将记录的地址赋给x.Pointer[i]
			
			x.Pointer[i] = record.Raddress;
			
			WriteBPlusNode(current ,x);
			
	  }
	  
}



void	BPlusTree :: Split_BPlus_Node(BPlusNode &father ,BPlusNode &current ,const int childnum)			//分裂满的B+树节点
{
	  int half = MAX_KEY/2 ;
	  
	  int i ;
	  
	  for(i = father.nkey ; i > childnum ; i -- )
	  {
			father.key[i] = father.key[i-1] ;
			father.Pointer[i+1] = father.Pointer[i];
	  }
	  father.nkey ++;
	  
	  BPlusNode t;
	  
	  FILEP address = GetBPlusNode();
	  
	  father.key[childnum] = current.key[half] ;
	  father.Pointer[childnum + 1] = address;
	  
	  for( i = half + 1 ; i < MAX_KEY ; i ++ )
	  {
			t.key[i-half-1] = current.key[i] ;
			t.Pointer[i-half-1] = current.Pointer[i];
	  }
	  
	  t.nkey = MAX_KEY - half - 1;
	  t.Pointer[t.nkey] = current.Pointer[MAX_KEY];
	  
	  t.isleaf = current.isleaf ;
	  
	  current.nkey = half ;
	  
	  if(current.isleaf )	//如果当前被分裂节点是叶子
	  {
			current.nkey ++;
			t.Pointer[MAX_KEY] = current.Pointer[MAX_KEY];
			current.Pointer[MAX_KEY] = address ;
	  }
	  
	  WriteBPlusNode(address ,t );
	  
}



void	BPlusTree :: Search_BPlus_Tree(TRecord &record ,SearchResult &result ) const		//在B+树查询一个关键字
{
	  int i;
	  
	  BPlusNode a;
	  FILEP current = ROOT;
	  
	  do
	  {
			ReadBPlusNode(current ,a );
			
			for(i = 0 ; i < a.nkey && record.key > a.key[i] ; i ++ );
			
			if( i < a.nkey && a.isleaf && record.key == a.key[i] )		//在B+树叶节点找到了等值的关键字
			{
				  result.Baddress = current;
				  result.Raddress = a.Pointer[i];						//返回该关键字所对应的记录的地址
				  result.exist = true;
				  
				  return ;
			}
			current = a.Pointer[i] ;
			
	  }while(!a.isleaf);
	  
	  result.exist = false;
}




void	BPlusTree :: delete_BPlus_tree(FILEP current ,TRecord &record )
{
	  int i , j;
	  
	  BPlusNode x;
	  ReadBPlusNode(current ,x );

	  
	  for(i = 0 ; i < x.nkey && record.key > x.key[i] ; i++ );
	  
	  if(i < x.nkey && x.key[i] == record.key )	//在当前节点找到关键字
	  {
			
			if(!x.isleaf)	  //在内节点找到关键字
			{
				  BPlusNode child;
				  ReadBPlusNode(x.Pointer[i] ,child );
				  
				  if( child.isleaf )	 //如果孩子是叶节点
				  {
						if(child.nkey > MAX_KEY/2 )		//情况A
						{
							  x.key[i] = child.key[child.nkey - 2];
							  child.nkey --;
							  
							  WriteBPlusNode(current ,x );
							  WriteBPlusNode(x.Pointer[i] ,child );
							  
							  return ;
						}
						else	//否则孩子节点的关键字数量不过半
						{
							  if(i > 0)		//有左兄弟节点
							  {
									BPlusNode lbchild;
									ReadBPlusNode(x.Pointer[i-1] ,lbchild );
									
									if(lbchild.nkey > MAX_KEY/2 )		//情况B
									{
										  for( j = child.nkey ; j > 0 ; j -- )
										  {
												child.key[j] = child.key[j-1];
												child.Pointer[j] = child.Pointer[j-1];
										  }
										  
										  child.key[0] = x.key[i-1];
										  child.Pointer[0] = lbchild.Pointer[lbchild.nkey-1];
										  
										  child.nkey ++;
										  
										  lbchild.nkey --;
										  
										  x.key[i-1] = lbchild.key[lbchild.nkey-1];
										  x.key[i] = child.key[child.nkey-2];
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  WriteBPlusNode(x.Pointer[i] ,child );
										  
									}
									else	//情况C
									{
										  for( j = 0 ; j < child.nkey ; j++ )
										  {
												lbchild.key[lbchild.nkey + j ] = child.key[j];
												lbchild.Pointer[lbchild.nkey + j ] = child.Pointer[j];
										  }
										  lbchild.nkey += child.nkey;
										  
										  lbchild.Pointer[MAX_KEY ] = child.Pointer[MAX_KEY];
										  
										  //释放child节点占用的空间x.Pointer[i]
										  
										  for( j = i - 1 ; j < x.nkey - 1; j ++)
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;

										  x.key[i-1] = lbchild.key[lbchild.nkey-2];
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  
										  i --;
										  
									}
									
									
							  }
							  else		//只有右兄弟节点
							  {
									BPlusNode rbchild;
									ReadBPlusNode(x.Pointer[i+1] ,rbchild );
									
									if(rbchild.nkey > MAX_KEY/2 )		//情况D
									{
										  x.key[i] = rbchild.key[0];
										  child.key[child.nkey] = rbchild.key[0];
										  child.Pointer[child.nkey] = rbchild.Pointer[0];
										  child.nkey ++;
										  
										  for( j = 0 ; j < rbchild.nkey - 1 ; j ++)
										  {
												rbchild.key[j] = rbchild.key[j+1];
												rbchild.Pointer[j] = rbchild.Pointer[j+1];
										  }
										  
										  rbchild.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i] ,child );
										  WriteBPlusNode(x.Pointer[i+1] ,rbchild );
										  
									}
									else	//情况E
									{
										  for( j = 0 ; j < rbchild.nkey ; j ++)
										  {
												child.key[child.nkey + j] = rbchild.key[j];
												child.Pointer[child.nkey +j] = rbchild.Pointer[j];
										  }
										  child.nkey += rbchild.nkey ;
										  
										  child.Pointer[MAX_KEY] = rbchild.Pointer[MAX_KEY];
										  
										  //释放rbchild占用的空间x.Pointer[i+1]
										  
										  for( j = i  ; j < x.nkey - 1; j ++)
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i] ,child );
										  
									}
									
							  }
							  
					  }
					  
				  }
				  else		//情况F
				  {
						
						//找到key在B+树叶节点的左兄弟关键字,将这个关键字取代key的位置
						
						TRecord trecord;
						trecord.key = record.key;
						SearchResult result;
						Search_BPlus_Tree(trecord ,result );
						
						BPlusNode last;
						
						ReadBPlusNode(result.Baddress ,last );
						
						x.key[i] = last.key[last.nkey - 2 ];
						
						WriteBPlusNode(current ,x);
						
					
						if(child.nkey > MAX_KEY/2 )		  //情况H
						{
							  
						}
						else		  //否则孩子节点的关键字数量不过半,则将兄弟节点的某一个关键字移至孩子
						{
							  if(i > 0 )  //x.key[i]有左兄弟
							  {
									BPlusNode lbchild;
									ReadBPlusNode(x.Pointer[i-1] ,lbchild );
									
									if( lbchild.nkey > MAX_KEY/2 )		//情况I
									{
										  for( j = child.nkey ; j > 0 ; j -- )
										  {
												child.key[j] = child.key[j-1];
												child.Pointer[j+1] = child.Pointer[j];
										  }
										  child.Pointer[1] = child.Pointer[0];
										  child.key[0] = x.key[i-1] ;
										  child.Pointer[0] = lbchild.Pointer[lbchild.nkey];
										  
										  child.nkey ++;
										  
										  x.key[i-1] = lbchild.key[lbchild.nkey-1] ;
										  lbchild.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  WriteBPlusNode(x.Pointer[i] ,child );
									}
									else		//情况J
									{
										  lbchild.key[lbchild.nkey] = x.key[i-1];	//将孩子节点复制到其左兄弟的末尾
										  lbchild.nkey ++;
										  
										  for(j = 0 ; j < child.nkey ; j++)		//将child节点拷贝到lbchild节点的末尾,
										  {
												lbchild.key[lbchild.nkey + j] = child.key[j] ;
												lbchild.Pointer[lbchild.nkey + j] = child.Pointer[j];
										  }
										  lbchild.Pointer[lbchild.nkey + j] = child.Pointer[j];
										  lbchild.nkey += child.nkey ;		  //已经将child拷贝到lbchild节点
										  
										  
										  //释放child节点的存储空间,x.Pointer[i]
										  
										  
										  //将找到关键字的孩子child与关键字左兄弟的孩子lbchild合并后,将该关键字前移,使当前节点的关键字减少一个
										  for(j = i - 1  ; j < x.nkey - 1 ; j++)
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  
										  i --;
										  
									}
									
							  }
							  else		  //否则x.key[i]只有右兄弟
							  {
									BPlusNode rbchild;
									ReadBPlusNode(x.Pointer[i+1] ,rbchild );
									
									if( rbchild.nkey > MAX_KEY/2 )	  //情况K
									{
										  
										  child.key[child.nkey] = x.key[i];
										  child.nkey ++;
										  
										  child.Pointer[child.nkey] = rbchild.Pointer[0];
										  x.key[i] = rbchild.key[0];
										  
										  for( j = 0 ; j < rbchild.nkey -1 ; j++)
										  {
												rbchild.key[j] = rbchild.key[j+1];
												rbchild.Pointer[j] = rbchild.Pointer[j+1];
										  }
										  rbchild.Pointer[j] = rbchild.Pointer[j+1];
										  rbchild.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i] ,child );
										  WriteBPlusNode(x.Pointer[i+1] ,rbchild );
										  
									}
									else		//情况L
									{
										  child.key[child.nkey] = x.key[i];
										  child.nkey ++;
										  
										  for(j = 0; j < rbchild.nkey ; j++)		//将rbchild节点合并到child节点后
										  {
												child.key[child.nkey + j] = rbchild.key[j];
												child.Pointer[child.nkey +j] = rbchild.Pointer[j];
										  }
										  child.Pointer[child.nkey +j] = rbchild.Pointer[j];
										  
										  child.nkey += rbchild.nkey;
										  
										  //释放rbchild节点所占用的空间,x,Pointer[i+1]
										  
										  for(j = i ;j < x.nkey - 1 ; j++ )	  //当前将关键字之后的关键字左移一位,使该节点的关键字数量减一
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i] ,child );
										  
									}
									
							  }
						}
						
				  }
				  
				  delete_BPlus_tree(x.Pointer[i] ,record );
				  
			}
			else  //情况G
			{
				  for( j = i ; j < x.nkey - 1 ; j ++ )
				  {
						x.key[j] = x.key[j+1];
						x.Pointer[j] = x.Pointer[j+1];
				  }
				  x.nkey-- ;
				  
				  WriteBPlusNode(current ,x);
				  
				  return ;
			}
			
	  }
	  else		  //在当前节点没找到关键字	  
	  {
			if(!x.isleaf )	  //没找到关键字,则关键字必然包含在以Pointer[i]为根的子树中
			{
				  BPlusNode child;
				  ReadBPlusNode(x.Pointer[i] ,child );
				  
				  if(!child.isleaf )	  //如果其孩子节点是内节点
				  {
						if(child.nkey > MAX_KEY/2 )		  //情况H
						{
							  
						}
						else		  //否则孩子节点的关键字数量不过半,则将兄弟节点的某一个关键字移至孩子
						{
							  if(i > 0 )  //x.key[i]有左兄弟
							  {
									BPlusNode lbchild;
									ReadBPlusNode(x.Pointer[i-1] ,lbchild );
									
									if( lbchild.nkey > MAX_KEY/2 )		//情况I
									{
										  for( j = child.nkey ; j > 0 ; j -- )
										  {
												child.key[j] = child.key[j-1];
												child.Pointer[j+1] = child.Pointer[j];
										  }
										  child.Pointer[1] = child.Pointer[0];
										  child.key[0] = x.key[i-1] ;
										  child.Pointer[0] = lbchild.Pointer[lbchild.nkey];
										  
										  child.nkey ++;
										  
										  x.key[i-1] = lbchild.key[lbchild.nkey-1] ;
										  lbchild.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  WriteBPlusNode(x.Pointer[i] ,child );
									}
									else		//情况J
									{
										  lbchild.key[lbchild.nkey] = x.key[i-1];	//将孩子节点复制到其左兄弟的末尾
										  lbchild.nkey ++;
										  
										  for(j = 0 ; j < child.nkey ; j++)		//将child节点拷贝到lbchild节点的末尾,
										  {
												lbchild.key[lbchild.nkey + j] = child.key[j] ;
												lbchild.Pointer[lbchild.nkey + j] = child.Pointer[j];
										  }
										  lbchild.Pointer[lbchild.nkey + j] = child.Pointer[j];
										  lbchild.nkey += child.nkey ;		  //已经将child拷贝到lbchild节点
										  
										  
										  //释放child节点的存储空间,x.Pointer[i]
										  
										  
										  //将找到关键字的孩子child与关键字左兄弟的孩子lbchild合并后,将该关键字前移,使当前节点的关键字减少一个
										  for(j = i - 1  ; j < x.nkey - 1 ; j++)
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  
										  i --;
										  
									}
									
							  }
							  else		  //否则x.key[i]只有右兄弟
							  {
									BPlusNode rbchild;
									ReadBPlusNode(x.Pointer[i+1] ,rbchild );
									
									if( rbchild.nkey > MAX_KEY/2 )	  //情况K
									{
										  
										  child.key[child.nkey] = x.key[i];
										  child.nkey ++;
										  
										  child.Pointer[child.nkey] = rbchild.Pointer[0];
										  x.key[i] = rbchild.key[0];
										  
										  for( j = 0 ; j < rbchild.nkey -1 ; j++)
										  {
												rbchild.key[j] = rbchild.key[j+1];
												rbchild.Pointer[j] = rbchild.Pointer[j+1];
										  }
										  rbchild.Pointer[j] = rbchild.Pointer[j+1];
										  rbchild.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i] ,child );
										  WriteBPlusNode(x.Pointer[i+1] ,rbchild );
										  
									}
									else		//情况L
									{
										  child.key[child.nkey] = x.key[i];
										  child.nkey ++;
										  
										  for(j = 0; j < rbchild.nkey ; j++)		//将rbchild节点合并到child节点后
										  {
												child.key[child.nkey + j] = rbchild.key[j];
												child.Pointer[child.nkey +j] = rbchild.Pointer[j];
										  }
										  child.Pointer[child.nkey +j] = rbchild.Pointer[j];
										  
										  child.nkey += rbchild.nkey;
										  
										  //释放rbchild节点所占用的空间,x,Pointer[i+1]
										  
										  for(j = i ;j < x.nkey - 1 ; j++ )	  //当前将关键字之后的关键字左移一位,使该节点的关键字数量减一
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;
										  
										  WriteBPlusNode(current ,x);
										  WriteBPlusNode(x.Pointer[i] ,child );
										  
									}
									
							  }
						}
				  }
				  else	//否则其孩子节点是外节点
				  {
						if(child.nkey > MAX_KEY/2 )  //情况M
						{
							  
						}
						else		//否则孩子节点不到半满
						{
							  if( i > 0 ) //有左兄弟
							  {
									BPlusNode lbchild;
									ReadBPlusNode(x.Pointer[i-1] ,lbchild );
									
									if( lbchild.nkey > MAX_KEY/2 )		//情况N
									{
										  for(j = child.nkey ; j > 0  ; j--)
										  {
												child.key[j] = child.key[j-1];
												child.Pointer[j] = child.Pointer[j-1];
										  }
										  child.key[0] = x.key[i-1];
										  child.Pointer[0] = lbchild.Pointer[lbchild.nkey-1];
										  child.nkey ++;
										  lbchild.nkey --;
										  
										  x.key[i-1] = lbchild.key[lbchild.nkey-1];
										  
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  WriteBPlusNode(x.Pointer[i] ,child );
										  WriteBPlusNode(current ,x );
										  
									}
									else		//情况O
									{
										  
										  for( j = 0 ; j < child.nkey ; j++ )		//与左兄弟孩子节点合并
										  {
												lbchild.key[lbchild.nkey + j ] = child.key[j] ;
												lbchild.Pointer[lbchild.nkey + j] = child.Pointer[j] ; 
										  }
										  lbchild.nkey += child.nkey ;
										  
										  lbchild.Pointer[MAX_KEY] = child.Pointer[MAX_KEY];
										  
										  //释放child占用的空间x.Pointer[i]
										  
										  for( j = i - 1; j < x.nkey - 1 ; j ++ )
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  
										  x.nkey --;
										  
										  WriteBPlusNode(x.Pointer[i-1] ,lbchild );
										  WriteBPlusNode(current ,x );
										  
										  i --;
										  
									}
									
							  }
							  else		  //否则只有右兄弟
							  {
									BPlusNode rbchild;
									ReadBPlusNode(x.Pointer[i+1] ,rbchild );
									
									if( rbchild.nkey > MAX_KEY/2 )		//情况P
									{
										  x.key[i] = rbchild.key[0] ;
										  child.key[child.nkey] = rbchild.key[0];
										  child.Pointer[child.nkey] = rbchild.Pointer[0];
										  child.nkey ++;
										  
										  for(j = 0 ; j < rbchild.nkey - 1 ; j ++)
										  {
												rbchild.key[j] = rbchild.key[j+1];
												rbchild.Pointer[j] = rbchild.Pointer[j+1];
										  }
										  rbchild.nkey --;
										  
										  WriteBPlusNode(current ,x );
										  WriteBPlusNode(x.Pointer[i+1] ,rbchild );
										  WriteBPlusNode(x.Pointer[i] ,child );
										  
									}
									else		//情况Q
									{
										  for(j = 0 ; j < rbchild.nkey ; j ++)
										  {
												child.key[child.nkey + j] = rbchild.key[j];
												child.Pointer[child.nkey + j] = rbchild.Pointer[j];
										  }
										  child.nkey += rbchild.nkey;
										  child.Pointer[MAX_KEY] = rbchild.Pointer[MAX_KEY];
										  
										  //释放rbchild占用的空间x.Pointer[i+1]
										  
										  for(j = i ; j < x.nkey - 1 ; j ++ )
										  {
												x.key[j] = x.key[j+1];
												x.Pointer[j+1] = x.Pointer[j+2];
										  }
										  x.nkey --;
										  
										  WriteBPlusNode(current ,x );
										  WriteBPlusNode(x.Pointer[i] ,child );
										  
										  
									}
									
							  }
							  
						}
						
				  }
				  
				  delete_BPlus_tree(x.Pointer[i] ,record );
			}
			
			
	  }
	  
	  
}



void	BPlusTree :: Delete_BPlus_Tree(TRecord &record )	//在B+中删除一个关键字
{
	  delete_BPlus_tree(ROOT ,record );
	  
	  BPlusNode rootnode;
	  ReadBPlusNode(ROOT ,rootnode );
	  
	  if( !rootnode.isleaf && rootnode.nkey == 0 )	  //如果删除关键字后根节点不是叶节点,并且关键字数量为0时根节点也应该被删除
	  {
			//释放ROOT节点占用的空间
			ROOT = rootnode.Pointer[0];			//根节点下移,B+树高度减1
			
	  }
	  
}




void  BPlusTree :: EnumLeafKey()	//依次枚举B+树叶节点的所有关键字
{
	  BPlusNode head;
	  
	  ReadBPlusNode(ROOT ,head );
	  
	  while(!head.isleaf )
	  {
			ReadBPlusNode(head.Pointer[0] ,head );
	  }
	  
	  while(1)
	  {
			for(int i = 0 ; i < head.nkey ; i ++)
				  printf("%d\n",head.key[i] );
			
			if(head.Pointer[MAX_KEY] == 0 )
				  break;
			
			ReadBPlusNode(head.Pointer[MAX_KEY] ,head );
	  }
	  
}




inline FILEP	BPlusTree :: GetBPlusNode()	 const //在磁盘上分配一块B+树节点空间
{
	  fseek(Bfile ,0 ,SEEK_END);
	  
	  return  ftell(Bfile );
}

inline void	BPlusTree :: ReadBPlusNode(const FILEP address ,BPlusNode	&r ) const //读取address地址上的一块B+树节点
{
	  fseek(Bfile ,address ,SEEK_SET );
	  fread((char*)(&r) ,sizeof(BPlusNode) ,1 ,Bfile);
	  
}


inline void	BPlusTree :: WriteBPlusNode(const FILEP address ,const BPlusNode &r ) //将一个B+树节点写入address地址
{
	  fseek(Bfile ,address ,SEEK_SET );
	  fwrite((char*)(&r) ,sizeof(BPlusNode) ,1 ,Bfile);
	  
}



int main()
{
	  BPlusTree tree;
	  
	  tree.Build_BPlus_Tree();		//建树
	  
	  TRecord record;	SearchResult result;
	  
	  int time1 = clock();
	  
	  int i;
	  for(i = 0 ; i < 100000 ; i ++ )
	  {
			record.key = i;
			tree.Insert_BPlus_Tree(record );
	  	//	printf("%d\n",i );
	  }
	  
	  for( i = 99997 ; i > 0; i--)
	  {
			record.key = i;
			tree.Delete_BPlus_Tree(record );
			tree.Search_BPlus_Tree(record ,result );
			if(result.exist )
				  break;
		//	printf("%d\n",i);
	  }

	  cout<<clock() - time1 <<endl;
	  system("pause");
	  tree.EnumLeafKey();
	  
	  tree.~BPlusTree();
	  system("pause");
	  return 0;
}


 

  • 5
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 21
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 21
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值