1. 程式人生 > >B+樹的實現

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;
}