1. 程式人生 > >B-樹的插入、查詢、刪除 及 可執行的C語言程式碼

B-樹的插入、查詢、刪除 及 可執行的C語言程式碼

 前面討論的查詢都是內查詢演算法,被查詢的資料都在記憶體。當查詢的資料放在外存,用平衡二叉樹作磁碟檔案的索引組織時,若以結點為內外存交換的單位,則找到需要的關鍵字之前,平均要進行lgn次磁碟讀操作,而磁碟、光碟的讀寫時間要比隨機存取的記憶體代價大得多。其二,外存的存取是以“頁”為單位的,一頁的大小通常是1024位元組或2048位元組。

 針對上述特點,1972R.BayerE.M.Cright提出了一種B-樹的多路平衡查詢樹,以適合磁碟等直接存取裝置上組織動態查詢表B-樹上演算法的執行時間主要由讀、寫磁碟的次數來決定,故一次I/O操作應讀寫儘可能多的資訊。因此B-樹的結點規模一般以一個磁碟頁為單位。

一個結點包含的關鍵字及其孩子個數取決於磁碟頁的大小。

一、基本概念

B-樹又稱為多路平衡查詢樹。

一棵度為mB-樹稱為mB_樹。一個結點有k個孩子時,必有k-1個關鍵字才能將子樹中所有關鍵字劃分為k個子集。B-樹中所有結點的孩子結點最大值稱為B-樹的階,通常用m表示。從查詢效率考慮,一般要求m3。一棵m階的B-樹或者是一棵空樹,或者是滿足下列要求的m叉樹:

1)根結點或者為葉子,或者至少有兩棵子樹,至多有m棵子樹。

2)除根結點外,所有非終端結點至少有ceil(m/2)棵子樹,至多有m棵子樹。

3)所有葉子結點都在樹的同一層上。

4)每個結點的結構為:

nA0K1A1K2

A2,…KnAn

其中,Ki(1in)為關鍵字,且Ki<Ki+1(1in-1)

Ai(0in)為指向子樹根結點的指標。且Ai所指子樹所有結點中的關鍵字均小於Ki+1An所指子樹中所有結點的關鍵字均大於Kn

n為結點中關鍵字的個數,滿足ceil(m/2)-1nm-1

比如,一棵3B-樹,m=3。它滿足:

1)每個結點的孩子個數小於等於3

2)除根結點外,其他結點至少有=2個孩子。

3根結點有兩個孩子結點

4)除根結點外的所有結點的n大於等於=1,小於等於2

5)所有葉結點都在同一層上。29、B-樹及其查詢 - 墨涵 - 墨涵天地

二、B-樹查詢的演算法思想

1B-樹的查詢

B-樹的查詢過程:根據給定值查詢結點和在結點的關鍵字中進行查詢交叉進行。首先從根結點開始重複如下過程:

若比結點的第一個關鍵字小,則查詢在該結點第一個指標指向的結點進行;若等於結點中某個關鍵字,則查詢成功;若在兩個關鍵字之間,則查詢在它們之間的指標指向的結點進行;若比該結點所有關鍵字大,則查詢在該結點最後一個指標指向的結點進行;若查詢已經到達某個葉結點,則說明給定值對應的資料記錄不存在,查詢失敗。

2.B-樹的插入

插入的過程分兩步完成:

1)利用前述的B-樹的查詢演算法查詢關鍵字的插入位置。若找到,則說明該關鍵字已經存在,直接返回。否則查詢操作必失敗於某個最低層的非終端結點上。

2)判斷該結點是否還有空位置。即判斷該結點的關鍵字總數是否滿足n<=m-1。若滿足,則說明該結點還有空位置,直接把關鍵字k插入到該結點的合適位置上。若不滿足,說明該結點己沒有空位置,需要把結點分裂成兩個。

分裂的方法是:生成一新結點。把原結點上的關鍵字和k按升序排序後,從中間位置把關鍵字(不包括中間位置的關鍵字)分成兩部分。左部分所含關鍵字放在舊結點中,右部分所含關鍵字放在新結點中,中間位置的關鍵字連同新結點的儲存位置插入到父結點中。如果父結點的關鍵字個數也超過(m-1),則要再分裂,再往上插。直至這個過程傳到根結點為止。

29、B-樹及其查詢 - 墨涵 - 墨涵天地
29、B-樹的插入、查詢、刪除 - EdwardLewis - 墨涵天地

29、B-樹及其查詢 - 墨涵 - 墨涵天地

29、B-樹及其查詢 - 墨涵 - 墨涵天地

3B-樹的刪除

B-樹上刪除關鍵字k的過程分兩步完成:

1)利用前述的B-樹的查詢演算法找出該關鍵字所在的結點。然後根據 k所在結點是否為葉子結點有不同的處理方法。

2)若該結點為非葉結點,且被刪關鍵字為該結點中第i個關鍵字key[i],則可從指標son[i]所指的子樹中找出最小關鍵字Y,代替key[i]的位置,然後在葉結點中刪去Y

因此,把在非葉結點刪除關鍵字k的問題就變成了刪除葉子結點中的關鍵字的問題了。

B-樹葉結點上刪除一個關鍵字的方法是

首先將要刪除的關鍵字 k直接從該葉子結點中刪除。然後根據不同情況分別作相應的處理,共有三種可能情況:

1)如果被刪關鍵字所在結點的原關鍵字個數n>=ceil(m/2),說明刪去該關鍵字後該結點仍滿足B-樹的定義。這種情況最為簡單,只需從該結點中直接刪去關鍵字即可。

2)如果被刪關鍵字所在結點的關鍵字個數n等於ceil(m/2)-1,說明刪去該關鍵字後該結點將不滿足B-樹的定義,需要調整。

調整過程為:如果其左右兄弟結點中有“多餘”的關鍵字,即與該結點相鄰的右(左)兄弟結點中的關鍵字數目大於ceil(m/2)-1。則可將右(左)兄弟結點中最小(大)關鍵字上移至雙親結點。而將雙親結點中小(大)於該上移關鍵字的關鍵字下移至被刪關鍵字所在結點中。

3)如果左右兄弟結點中沒有“多餘”的關鍵字,即與該結點相鄰的右(左)兄弟結點中的關鍵字數目均等於ceil(m/2)-1。這種情況比較複雜。需把要刪除關鍵字的結點與其左(或右)兄弟結點以及雙親結點中分割二者的關鍵字合併成一個結點,即在刪除關鍵字後,該結點中剩餘的關鍵字加指標,加上雙親結點中的關鍵字Ki一起,合併到Ai(是雙親結點指向該刪除關鍵字結點的左(右)兄弟結點的指標)所指的兄弟結點中去。如果因此使雙親結點中關鍵字個數小於ceil(m/2)-1,則對此雙親結點做同樣處理。以致於可能直到對根結點做這樣的處理而使整個樹減少一層。

總之,設所刪關鍵字為非終端結點中的Ki,則可以指標Ai所指子樹中的最小關鍵字Y代替Ki,然後在相應結點中刪除Y。對任意關鍵字的刪除都可以轉化為對最下層關鍵字的刪除。

29、B-樹及其查詢 - 墨涵 - 墨涵天地

如圖示:

a被刪關鍵字Ki所在結點的關鍵字數目不小於ceil(m/2),則只需從結點中刪除Ki和相應指標Ai,樹的其它部分不變。

29、B-樹及其查詢 - 墨涵 - 墨涵天地

b、被刪關鍵字Ki所在結點的關鍵字數目等於ceil(m/2)-1,則需調整。調整過程如上面所述。

29、B-樹及其查詢 - 墨涵 - 墨涵天地

c、被刪關鍵字Ki所在結點和其相鄰兄弟結點中的的關鍵字數目均等於ceil(m/2)-1,假設該結點有右兄弟,且其右兄弟結點地址由其雙親結點指標Ai所指。則在刪除關鍵字之後,它所在結點的剩餘關鍵字和指標,加上雙親結點中的關鍵字Ki一起,合併到Ai所指兄弟結點中(若無右兄弟,則合併到左兄弟結點中)。如果因此使雙親結點中的關鍵字數目少於ceil(m/2)-1,則依次類推。

29、B-樹及其查詢 - 墨涵 - 墨涵天地

29、B-樹及其查詢 - 墨涵 - 墨涵天地

三、B-樹的C語言描述

1、儲存結構

29、B-樹及其查詢 - 墨涵 - 墨涵天地

29、B-樹及其查詢 - 墨涵 - 墨涵天地

2、插入

29、B-樹及其查詢 - 墨涵 - 墨涵天地

3、查詢

29、B-樹及其查詢 - 墨涵 - 墨涵天地

四、B-樹的C語言實現

#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#define OK 1
#define ERROR -1
#define m 3 //3階樹
#define N 16 //資料元素個數
#define MAX 5 //字串最大長度+1
typedef int KeyType;
struct Others  //記錄的其它部分
{
char info[MAX];
};
struct Record
{
KeyType key; //關鍵字
Others others; //其它部分
};
typedef struct BTNode
{
int keynum; //結點中關鍵字個數
BTNode *parent;//指向雙親節點
   struct Node  //結點向量型別
   {
   KeyType key; //關鍵字向量
   BTNode *ptr;//子樹指標向量
   Record *recptr; //記錄向量指標
   }node[m+1]; //key,recptr的0號單元未用
}BTNode,*BTree;
struct Result //B樹的查詢結果型別
{
BTNode *pt; //指向找到的結點
int i; //在節點中關鍵字序號,1...m
int tag; //1表示查詢成功,0表示查詢失敗。
};

int InitDSTable(BTree &DT)
{
DT=NULL;
return OK;
}//InitDSTable

void DestroyDSTable(BTree &DT)
{
int i;
if(DT) //非空樹
    {
     for(i=0;i<=DT->keynum;i++)
         DestroyDSTable(DT->node[i].ptr);
     free(DT);
     DT=NULL;
    }//if
}//DestroyDSTable

int Search(BTree p,KeyType K)
{//在p->node[1...keytype].key中查詢i,使得p->node[i].key<=K<
    //p->node[i+1].key
    int i=0,j;
    for(j=1;j<=p->keynum;j++)
        if(p->node[j].key<=K)
            i=j;
    return i;
}//Search

void Insert(BTree &q,int i,Record *r,BTree ap)
{//將r->key、r和ap分別插入到q->key[i+1]、
    //q->recptr[              i+1]和q->ptr[i+1]中
    int j;
    for(j=q->keynum;j>i;j--) //空出q->node[i+1]
     q->node[j+1]=q->node[j];
    q->node[i+1].key=r->key;
    q->node[i+1].ptr=ap; //前加入的結點,還沒有兒子結點
    q->node[i+1].recptr=r;
    q->keynum++;
}//Insert

void NewRoot(BTree &T,Record *r,BTree ap)
{// 生成含資訊(T,r,ap)的新的根結點*T,原T和ap為子樹指標
BTree p;
p=(BTree)malloc(sizeof(BTNode));
p->node[0].ptr=T;
T=p;
if(T->node[0].ptr)
    T->node[0].ptr->parent=T;
T->parent=NULL;
T->keynum=1;
T->node[1].key=r->key;
T->node[1].recptr=r;
T->node[1].ptr=ap;
if(T->node[1].ptr)
    T->node[1].ptr->parent=T;
}//NewRoot

void split(BTree &q,BTree &ap)
{// 將結點q分裂成兩個結點,前一半保留,後一半移入新生結點ap
int i,s=(m+1)/2;
ap=(BTree)malloc(sizeof(BTNode));//生成新結點ap
ap->node[0].ptr=q->node[s].ptr;//原來結點中間位置關鍵字相應指標指向的子樹放到
                               //新生成結點的0棵子樹中去
for(i=s+1;i<=m;i++) //後一半移入ap
   {
   ap->node[i-s]=q->node[i];
   if(ap->node[i-s].ptr)
       ap->node[i-s].ptr->parent=ap;
   }//for
   ap->keynum=m-s;
   ap->parent=q->parent;
   q->keynum=s-1; // q的前一半保留,修改keynum
}//split

void InsertBTree(BTree &T,Record *r,BTree q,int i)
{//在m階B樹T上結點*q的key[i]與key[i+1]之間插入關鍵字K的指標r。若引起
   // 結點過大,則沿雙親鏈進行必要的結點分裂調整,使T仍是m階B樹。
BTree ap=NULL;
int finished=false;
int s;
Record *rx;
rx=r;
while(q&&!finished)
   {
    Insert(q,i,rx,ap);//將r->key、r和ap分別插入到q->key[i+1]、
                      //q->recptr[i+1]和q->ptr[i+1]中
    if(q->keynum<m)
        finished=true;
    else
      {//分裂結點*q
      s=(m+1)/2;
      rx=q->node[s].recptr;
      split(q,ap);//將q->key[s+1..m],q->ptr[s..m]和q->recptr[s+1..m]
                  //移入新結點*ap
      q=q->parent;
      if(q)
          i=Search(q,rx->key);//在雙親結點*q中查詢rx->key的插入位置
      }//else
   }//while
if(!finished) //T是空樹(引數q初值為NULL)或根結點已分裂為
              //結點*q和*ap
NewRoot(T,rx,ap);    
}//InsertBTree

Result SearchBTree(BTree T,KeyType K)
{// 在m階B樹T上查詢關鍵字K,返回結果(pt,i,tag)。若查詢成功,則特徵值
// tag=1,指標pt所指結點中第i個關鍵字等於K;否則特徵值tag=0,等於K的
// 關鍵字應插入在指標Pt所指結點中第i和第i+1個關鍵字之間。
BTree p=T,q=NULL; //初始化,p指向待查結點,q指向p的雙親
int found=false;
int i=0;
Result r;
while(p&&!found)
   {
   i=Search(p,K);//p->node[i].key≤K<p->node[i+1].key
   if(i>0&&p->node[i].key==K)
       found=true;
   else
     {
     q=p;
     p=p->node[i].ptr;//在子樹中繼續查詢
     }//else
    }//while
   r.i=i;
   if(found)
     {
      r.pt=p;
      r.tag=1;
     }//if
   else
      {
       r.pt=q;
       r.tag=0;
      }//else
    return r;
}//SearchBTree

void print(BTNode c,int i) // TraverseDSTable()呼叫的函式
 {
   printf("(%d,%s)",c.node[i].key,c.node[i].recptr->others.info);
 }//print
void TraverseDSTable(BTree DT,void(*Visit)(BTNode,int))
{// 初始條件: 動態查詢表DT存在,Visit是對結點操作的應用函式
// 操作結果: 按關鍵字的順序對DT的每個結點呼叫函式Visit()一次且至多一次
int i;
if(DT) //非空樹
    {
      if(DT->node[0].ptr) // 有第0棵子樹
         TraverseDSTable(DT->node[0].ptr,Visit);
      for(i=1;i<=DT->keynum;i++)
        {
         Visit(*DT,i);
         if(DT->node[i].ptr) // 有第i棵子樹
         TraverseDSTable(DT->node[i].ptr,Visit);
        }//for
    }//if
}//TraverseDSTable

void InputBR(BTree &t,Record r[])
{
Result s;    
for(int i=0;i<N;i++)
   {
     s=SearchBTree(t,r[i].key);
     if(!s.tag)
       InsertBTree(t,&r[i],s.pt,s.i);
   }
}//InputBR
void UserSearch(BTree t)
{
int i;
Result s;
printf("\n請輸入待查詢記錄的關鍵字: ");
scanf("%d",&i);
s=SearchBTree(t,i);
if(s.tag)
print(*(s.pt),s.i);
else
printf("沒找到");
printf("\n");
}//UserSearch
void DeleteIt(BTree t,BTNode *dnode,int id)
{
if(dnode->keynum>=ceil(m/2))
   {
    dnode->keynum--;
    dnode->node[id].ptr=NULL;
   }//if被刪關鍵字Ki所在結點的關鍵字數目不小於ceil(m/2),則只需從結點中刪除Ki和相應指標Ai,樹的其它部分不變。
else if((dnode->keynum==(ceil(m/2)-1))&&((id+1)<(m-1))&&dnode->parent->node[id+1].ptr->keynum>(ceil(m/2)-1))
   {
    for(int i=1;i<m&&dnode->parent->node[i].key < dnode->parent->node[id+1].ptr->node[1].key;i++)
        dnode->node[i].key=dnode->parent->node[i].key;
    dnode->parent->node[1].key=dnode->parent->node[id+1].ptr->node[1].key;
    (dnode->parent->node[id+1].ptr->keynum)--;
   }//else if 被刪關鍵字Ki所在結點的關鍵字數目等於ceil(m/2)-1,則需調整。本次為與右兄弟調整
else if((dnode->keynum==(ceil(m/2)-1))&&((id-1)>0    )&&dnode->parent->node[id-1].ptr->keynum>(ceil(m/2)-1))
   {
    for(int i=1;i<m&&dnode->parent->node[i].key > dnode->parent->node[id-1].ptr->node[dnode->parent->node[id-1].ptr->keynum].key;i++)
        dnode->node[i].key=dnode->parent->node[i].key;
    dnode->parent->node[1].key=dnode->parent->node[id-1].ptr->node[dnode->parent->node[id-1].ptr->keynum].key;
    (dnode->parent->node[id-1].ptr->keynum)--;
   }//2-else if被刪關鍵字Ki所在結點的關鍵字數目等於ceil(m/2)-1,則需調整。本次為與左兄弟調整
else if((dnode->keynum==(ceil(m/2)-1))&&((id+1)<(m-1))&&dnode->parent->node[id+1].ptr->keynum==(ceil(m/2)-1))
   {
    do
      {
        BTree tmp;
        tmp=dnode;
       dnode->parent->node[id+1].ptr->node[2]=dnode->parent->node[id+1].ptr->node[1];
       dnode->parent->node[id+1].ptr->node[1]=dnode->parent->node[1];
       dnode->parent->node[id+1].ptr->keynum++;
       dnode->parent->node[id+1].ptr->node[0].ptr=dnode->node[1].ptr;
       dnode->parent->keynum--;
       dnode->parent->node[id].ptr=NULL;
       tmp=dnode;
       if(dnode->parent->keynum>=(ceil(m/2)-1))
           dnode->parent->node[1]=dnode->parent->node[2];
       dnode=dnode->parent;
       free(tmp);
      }while(dnode->keynum<(ceil(m/2)-1));    //雙親中keynum<
   }//3-else if被刪關鍵字Ki所在結點和其相鄰兄弟結點中的的關鍵字數目均等於ceil(m/2)-1,本次假設右兄弟存在
else if((dnode->keynum==(ceil(m/2)-1))&&(id-1)>0      &&dnode->parent->node[id-1].ptr->keynum==(ceil(m/2)-1))
   {
    do
      {
        BTree tmp;
        tmp=dnode;
       dnode->parent->node[id-1].ptr->node[2]=dnode->parent->node[id-1].ptr->node[1];
       dnode->parent->node[id-1].ptr->node[1]=dnode->parent->node[1];
       dnode->parent->node[id-1].ptr->keynum++;
       dnode->parent->node[id-1].ptr->node[0].ptr=dnode->node[1].ptr;
       dnode->parent->keynum--;
       dnode->parent->node[id].ptr=NULL;
       tmp=dnode;
       if(dnode->parent->keynum>=(ceil(m/2)-1))
           dnode->parent->node[1]=dnode->parent->node[2];
       dnode=dnode->parent;
       free(tmp);
      }while(dnode->keynum<(ceil(m/2)-1)); //雙親中keynum<
   }//4-else if被刪關鍵字Ki所在結點和其相鄰兄弟結點中的的關鍵字數目均等於ceil(m/2)-1,本次假設左兄弟存在
    else printf("Error!"); //出現異常
}//DeleteIt
void UserDelete(BTree t)
{
KeyType date;
Result s;
printf("Please input the date you want to delete:\n");
scanf("%d",&date);
s=SearchBTree(t,date);
if(!s.tag)  printf("Search failed,no such date\n");
else DeleteIt(t,s.pt,s.i);
}//UserDelete

int main()
{
Record r[N]={{24,"1"},{45,"2"},{53,"3"},{12,"4"},{37,"5"},
        {50,"6"},{61,"7"},{90,"8"},{100,"9"},{70,"10"},
        {3,"11"},{30,"12"},{26,"13"},{85,"14"},{3,"15"},
        {7,"16"}};    
BTree t;
InitDSTable(t);
InputBR(t,r);
printf("按關鍵字的順序遍歷B_樹:\n");
TraverseDSTable(t,print);
UserSearch(t);
UserDelete(t);
TraverseDSTable(t,print);
DestroyDSTable(t);
return 1;
}

五、複雜度分析

B-樹查詢包含兩種基本動作:

●在B-樹上查詢結點

●在結點中找關鍵字

前一操作在磁碟上進行,後一操作在記憶體進行。因此查詢效率主要由前一操作決定。在磁碟上查詢的次數取決於關鍵字結點在B-樹上的層次數。

定理:若n1m3,則對任意一棵具有n個關鍵字的mB-樹,其樹高度h至多為logt((n+1)/2)+1t= ceil(m/2)。也就是說根結點到關鍵字所在結點的路徑上涉及的結點數不超過logt((n+1)/2)+1。推理如下:

29、B-樹的插入、查詢、刪除 - 墨涵 - 墨涵天地

29、B-樹的插入、查詢、刪除 - 墨涵 - 墨涵天地

29、B-樹的插入、查詢、刪除 - 墨涵 - 墨涵天地

另一套程式碼,可執行,附帶時間測試函式。

/* btrees.h */ 
/* 
* 平衡多路樹的一種重要方案。 
* 在 1970 年由 R. Bayer 和 E. McCreight 發明。 
*/ 

#include <stdlib.h> 
#include <stdio.h> 
#include <time.h>
#include <assert.h>
#include <windows.h>

#define M 1
/* B 樹的階,即非根節點中鍵的最小數目。 
* 有些人把階定義為非根節點中子樹的最大數目。 
*/ 







typedef int typekey; 
typedef struct btnode {    /* B-Tree 節點 */ 
	int d;    /* 節點中鍵的數目 */ 
	typekey k[M+2];    /* 鍵 */ 
	char *v[M+2];    /* 值 */ 
	struct btnode *p[M+2+1];    /* 指向子樹的指標 */ 
} node, *btree; 
/* 
* 每個鍵的左子樹中的所有的鍵都小於這個鍵, 
* 每個鍵的右子樹中的所有的鍵都大於等於這個鍵。 
* 葉子節點中的每個鍵都沒有子樹。 
*/ 

/* 當 M 等於 1 時也稱為 2-3 樹 
*    +----+----+ 
*    | k0 | k1 |                     
*  +-+----+----+--- 
*  | p0 | p1 | p2 | 
*  +----+----+----+ 
*/ 
//extern int btree_disp; /* 查詢時找到的鍵在節點中的位置 */ 
//extern char * InsValue; /* 與要插的鍵相對應的值 */ 


int btree_disp; /* 查詢時找到的鍵在節點中的位置 */ 
char * InsValue; /* 與要插的鍵相對應的值 */ 
int flag; /* 節點增減標誌 */ 
int btree_level; /* 多路樹的高度 */ 
int btree_count; /* 多路樹的鍵總數 */ 
int node_sum;  /* 多路樹的節點總數 */ 
int level; /* 當前訪問的節點所處的高度 */ 
btree NewTree; /* 在節點分割的時候指向新建的節點 */ 
typekey InsKey; /* 要插入的鍵 */ 




btree search(typekey, btree); 
btree insert(typekey,btree); 
btree delete(typekey,btree); 
int height(btree); 
int count(btree); 
double payload(btree); 
btree deltree(btree); 

static void InternalInsert(typekey, btree); 
static void InsInNode(btree, int); 
static void SplitNode(btree, int); 
static btree NewRoot(btree); 

static void InternalDelete(typekey, btree); 
static void JoinNode(btree, int); 
static void MoveLeftNode(btree t, int); 
static void MoveRightNode(btree t, int); 
static void DelFromNode(btree t, int); 
static btree FreeRoot(btree); 

static btree delall(btree); 
static void Error(int,typekey); 




void out_txt(int * pt,int n);


int *read_txt(int n);

/* end of btrees.h */ 
/* btrees.c */ 

#include "btrees.h"
btree search(typekey key, btree t) 
{ 
	int i,j,m; 
	level=btree_level-1; 
	while (level >= 0){ 
		for(i=0, j=t->d-1; i<j; m=(j+i)/2, (key > t->k[m])?(i=m+1):(j=m)); 
		if (key == t->k [ i ]){ 
			btree_disp = i; 
			return t; 
		} 
		if (key > t->k [ i ]) /* i == t->d-1 時有可能出現 */ 
			i++; 
		t = t->p[ i ]; 
		level--; 
	} 
	return NULL; 
} 

btree insert(typekey key, btree t) 
{ 
	level=btree_level; 
	InternalInsert(key, t); 
	if (flag == 1)  /* 根節點滿之後,它被分割成兩個半滿節點 */ 
		t=NewRoot(t);    /* 樹的高度增加 */ 
	return t; 
} 

void InternalInsert(typekey key, btree t) 
{ 
	int i,j,m; 

	level--; 
	if (level < 0){ /* 到達了樹的底部: 指出要做的插入 */ 
		NewTree = NULL; /* 這個鍵沒有對應的子樹 */ 
		InsKey = key; /* 導致底層的葉子節點增加鍵值+空子樹對 */ 
		btree_count++; 
		flag = 1; /* 指示上層節點把返回的鍵插入其中 */ 
		return; 
	} 
	for(i=0, j=t->d-1; i<j; m=(j+i)/2, (key > t->k[m])?(i=m+1):(j=m)); 
	if (key == t->k[ i ]) { 
		Error(1,key); /* 鍵已經在樹中 */ 
		flag = 0; 
		return; 
	} 
	if (key > t->k[ i ]) /* i == t->d-1 時有可能出現 */ 
		i++; 
	InternalInsert(key, t->p[ i ]); 

	if (flag == 0) 
		return; 
	/* 有新鍵要插入到當前節點中 */ 
	if (t->d < 2*M) {/* 當前節點未滿 */ 
		InsInNode(t, i); /* 把鍵值+子樹對插入當前節點中 */ 
		flag = 0; /* 指示上層節點沒有需要插入的鍵值+子樹,插入過程結束 */ 
	} 
	else /* 當前節點已滿,則分割這個頁面並把鍵值+子樹對插入當前節點中 */ 
		SplitNode(t, i); /* 繼續指示上層節點把返回的鍵值+子樹插入其中 */ 
} 

/* 
* 把一個鍵和對應的右子樹插入一個節點中 
*/ 
void InsInNode(btree t, int d) 
{ 
	int i; 
	/* 把所有大於要插入的鍵值的鍵和對應的右子樹右移 */ 
	for(i = t->d; i > d; i--){ 
		t->k[ i ] = t->k[i-1]; 
		t->v[ i ] = t->v[i-1]; 
		t->p[i+1] = t->p[ i ]; 
	} 
	/* 插入鍵和右子樹 */ 
	t->k[ i ] = InsKey; 
	t->p[i+1] = NewTree; 
	t->v[ i ] = InsValue; 
	t->d++; 
} 
/* 
* 前件是要插入一個鍵和對應的右子樹,並且本節點已經滿 
* 導致分割這個節點,插入鍵和對應的右子樹, 
* 並向上層返回一個要插入鍵和對應的右子樹 
*/ 
void SplitNode(btree t, int d) 
{     
	int i,j; 
	btree temp; 
	typekey temp_k; 
	char *temp_v; 
	/* 建立新節點 */ 
	temp = (btree)malloc(sizeof(node)); 
	/* 
	*   +---+--------+-----+-----+--------+-----+ 
	*   | 0 | ...... |  M  | M+1 | ...... |2*M-1| 
	*   +---+--------+-----+-----+--------+-----+ 
	*   |<-      M+1     ->|<-        M-1     ->|   
	*/ 
	if (d > M) { /* 要插入當前節點的右半部分 */ 
		/* 把從 2*M-1 到 M+1 的 M-1 個鍵值+子樹對轉移到新節點中, 
		* 並且為要插入的鍵值+子樹空出位置 */ 
		for(i=2*M-1,j=M-1; i>=d; i--,j--) { 
			temp->k[j] = t->k[ i ]; 
			temp->v[j] = t->v[ i ]; 
			temp->p[j+1] = t->p[i+1]; 
		} 
		for(i=d-1,j=d-M-2; j>=0; i--,j--) { 
			temp->k[j] = t->k[ i ]; 
			temp->v[j] = t->v[ i ]; 
			temp->p[j+1] = t->p[i+1]; 
		} 
		/* 把節點的最右子樹轉移成新節點的最左子樹 */ 
		temp->p[0] = t->p[M+1]; 
		/* 在新節點中插入鍵和右子樹 */ 
		temp->k[d-M-1] = InsKey; 
		temp->p[d-M] = NewTree; 
		temp->v[d-M-1] = InsValue; 
		/* 設定要插入上層節點的鍵和值 */ 
		InsKey = t->k[M]; 
		InsValue = t->v[M]; 

	} 
	else { /* d <= M */ 
		/* 把從 2*M-1 到 M 的 M 個鍵值+子樹對轉移到新節點中 */ 
		for(i=2*M-1,j=M-1; j>=0; i--,j--) { 
			temp->k[j] = t->k[ i ]; 
			temp->v[j] = t->v[ i ]; 
			temp->p[j+1] = t->p[i+1]; 
		} 
		if (d == M) /* 要插入當前節點的正中間 */ 
			/* 把要插入的子樹作為新節點的最左子樹 */ 
			temp->p[0] = NewTree; 
		/* 直接把要插入的鍵和值返回給上層節點 */ 
		else { /* (d<M) 要插入當前節點的左半部分 */ 
			/* 把節點當前的最右子樹轉移成新節點的最左子樹 */ 
			temp->p[0] = t->p[M]; 
			/* 儲存要插入上層節點的鍵和值 */ 
			temp_k = t->k[M-1]; 
			temp_v = t->v[M-1]; 
			/* 把所有大於要插入的鍵值的鍵和對應的右子樹右移 */ 
			for(i=M-1; i>d; i--) { 
				t->k[ i ] = t->k[i-1]; 
				t->v[ i ] = t->v[i-1]; 
				t->p[i+1] = t->p[ i ]; 
			} 
			/* 在節點中插入鍵和右子樹 */ 
			t->k[d] = InsKey; 
			t->p[d+1] = NewTree; 
			t->v[d] = InsValue; 
			/* 設定要插入上層節點的鍵和值 */ 
			InsKey = temp_k; 
			InsValue = temp_v; 
		} 
	} 
	t->d =M; 
	temp->d = M; 
	NewTree = temp; 
	node_sum++; 
} 

btree delete(typekey key, btree t) 
{ 
	level=btree_level; 
	InternalDelete(key, t); 
	if (t->d == 0) 
		/* 根節點的子節點合併導致根節點鍵的數目隨之減少, 
		* 當根節點中沒有鍵的時候,只有它的最左子樹可能非空 */ 
		t=FreeRoot(t); 
	return t; 
} 

void InternalDelete(typekey key, btree t) 
{ 
	int i,j,m; 
	btree l,r; 
	int lvl; 

	level--; 
	if (level < 0) { 
		Error(0,key); /* 在整個樹中未找到要刪除的鍵 */ 
		flag = 0; 
		return; 
	} 
	for(i=0, j=t->d-1; i<j; m=(j+i)/2, (key > t->k[m])?(i=m+1):(j=m)); 
	if (key == t->k[ i ]) { /* 找到要刪除的鍵 */ 
		if (t->v[ i ] != NULL) 
			free(t->v[ i ]); /* 釋放這個節點包含的值 */ 
		if (level == 0) { /* 有子樹為空則這個鍵位於葉子節點 */ 
			DelFromNode(t,i); 
			btree_count--; 
			flag = 1; 
			/* 指示上層節點本子樹的鍵數量減少 */ 
			return; 
		} else { /* 這個鍵位於非葉節點 */ 
			lvl = level-1; 
			/* 找到前驅節點 */ 
			r = t->p[ i ]; 
			while (lvl > 0)  { 
				r = r->p[r->d]; 
				lvl--; 
			} 
			t->k[ i ]=r->k[r->d-1]; 
			t->v[ i ]=r->v[r->d-1]; 
			r->v[r->d-1]=NULL; 
			key = r->k[r->d-1]; 
		} 
	} 
	else if (key > t->k[ i ]) /* i == t->d-1 時有可能出現 */ 
		i++;          
	InternalDelete(key,t->p[ i ]); 
	/* 調整平衡 */ 
	if (flag == 0) 
		return; 
	if (t->p[ i ]->d < M) { 
		if (i == t->d) /* 在最右子樹中發生了刪除 */ 
			i--; /* 調整最右鍵的左右子樹平衡 */ 
		l = t->p [ i ]; 
		r = t->p[i+1]; 
		if (r->d > M) 
			MoveLeftNode(t,i); 
		else if(l->d > M) 
			MoveRightNode(t,i); 
		else { 
			JoinNode(t,i); 
			/* 繼續指示上層節點本子樹的鍵數量減少 */ 
			return; 
		} 
		flag = 0; 
		/* 指示上層節點本子樹的鍵數量沒有減少,刪除過程結束 */ 
	} 
} 

/* 
* 合併一個節點的某個鍵對應的兩個子樹 
*/ 
void JoinNode(btree t, int d) 
{ 
	btree l,r; 
	int i,j; 
	l = t->p[d]; 
	r = t->p[d+1]; 

	/* 把這個鍵下移到它的左子樹 */ 
	l->k[l->d] = t->k[d]; 
	l->v[l->d] = t->v[d]; 
	/* 把右子樹中的所有鍵值和子樹轉移到左子樹 */ 
	for (j=r->d-1,i=l->d+r->d; j >= 0 ; j--,i--) { 
		l->k[ i ] = r->k[j]; 
		l->v[ i ] = r->v[j]; 
		l->p[ i ] = r->p[j]; 
	} 
	l->p[l->d+r->d+1] = r->p[r->d]; 
	l->d += r->d+1; 
	/* 釋放右子樹的節點 */ 
	free(r); 
	/* 把這個鍵右邊的鍵和對應的右子樹左移 */ 
	for (i=d; i < t->d-1; i++) { 
		t->k[ i ] = t->k[i+1]; 
		t->v[ i ] = t->v[i+1]; 
		t->p[i+1] = t->p[i+2]; 
	} 
	t->d--; 
	node_sum--; 
} 
/* 
* 從一個鍵的右子樹向左子樹轉移一些鍵,使兩個子樹平衡 
*/ 
void MoveLeftNode(btree t, int d) 
{ 
	btree l,r; 
	int m; /* 應轉移的鍵的數目 */ 
	int i,j; 
	l = t->p[d]; 
	r = t->p[d+1]; 
	m = (r->d - l->d)/2; 

	/* 把這個鍵下移到它的左子樹 */ 
	l->k[l->d] = t->k[d]; 
	l->v[l->d] = t->v[d]; 
	/* 把右子樹的最左子樹轉移成左子樹的最右子樹 
	* 從右子樹向左子樹移動 m-1 個鍵+子樹對 */ 
	for (j=m-2,i=l->d+m-1; j >= 0; j--,i--) { 
		l->k[ i ] = r->k[j]; 
		l->v[ i ] = r->v[j]; 
		l->p[ i ] = r->p[j]; 
	} 
	l->p[l->d+m] = r->p[m-1]; 
	/* 把右子樹的最左鍵提升到這個鍵的位置上 */ 
	t->k[d] = r->k[m-1]; 
	t->v[d] = r->v[m-1]; 
	/* 把右子樹中的所有鍵值和子樹左移 m 個位置 */ 
	r->p[0] = r->p[m]; 
	for (i=0; i<r->d-m; i++) { 
		r->k[ i ] = r->k[i+m]; 
		r->v[ i ] = r->v[i+m]; 
		r->p[ i ] = r->p[i+m]; 
	} 
	r->p[r->d-m] = r->p[r->d]; 
	l->d+=m; 
	r->d-=m; 
} 
/* 
* 從一個鍵的左子樹向右子樹轉移一些鍵,使兩個子樹平衡 
*/ 
void MoveRightNode(btree t, int d) 
{ 
	btree l,r; 
	int m; /* 應轉移的鍵的數目 */ 
	int i,j; 
	l = t->p[d]; 
	r = t->p[d+1]; 

	m = (l->d - r->d)/2; 
	/* 把右子樹中的所有鍵值和子樹右移 m 個位置 */ 
	r->p[r->d+m]=r->p[r->d]; 
	for (i=r->d-1; i>=0; i--) { 
		r->k[i+m] = r->k[ i ]; 
		r->v[i+m] = r->v[ i ]; 
		r->p[i+m] = r->p[ i ]; 
	} 
	/* 把這個鍵下移到它的右子樹 */ 
	r->k[m-1] = t->k[d]; 
	r->v[m-1] = t->v[d]; 
	/* 把左子樹的最右子樹轉移成右子樹的最左子樹 */ 
	r->p[m-1] = l->p[l->d]; 
	/* 從左子樹向右子樹移動 m-1 個鍵+子樹對 */ 
	for (i=l->d-1,j=m-2; j>=0; j--,i--) { 
		r->k[j] = l->k[ i ]; 
		r->v[j] = l->v[ i ]; 
		r->p[j] = l->p[ i ]; 
	} 
	/* 把左子樹的最右鍵提升到這個鍵的位置上 */ 
	t->k[d] = l->k[ i ]; 
	t->v[d] = l->v[ i ]; 
	l->d-=m; 
	r->d+=m; 
} 
/* 
* 把一個鍵和對應的右子樹從一個節點中刪除 
*/ 
void DelFromNode(btree t, int d) 
{ 
	int i; 
	/* 把所有大於要刪除的鍵值的鍵左移 */ 
	for(i=d; i < t->d-1; i++) { 
		t->k[ i ] = t->k[i+1]; 
		t->v[ i ] = t->v[i+1]; 
	} 
	t->d--; 
} 
/* 
* 建立有兩個子樹和一個鍵的根節點 
*/ 
btree NewRoot(btree t) 
{ 
	btree temp; 
	temp = (btree)malloc(sizeof(node)); 
	temp->d = 1; 
	temp->p[0] = t; 
	temp->p[1] = NewTree; 
	temp->k[0] = InsKey; 
	temp->v[0] = InsValue; 
	btree_level++; 
	node_sum++; 
	return(temp); 
} 
/* 
* 釋放根節點,並返回它的最左子樹 
*/ 
btree FreeRoot(btree t) 
{ 
	btree temp; 
	temp = t->p[0]; 
	free(t); 
	btree_level--; 
	node_sum--; 
	return temp; 
} 

void Error(int f,typekey key) 
{ 
	if (f) 
		printf("Btrees error: Insert %d!\n",key); 
	else 
		printf("Btrees error: delete %d!\n",key); 
} 

int height(btree t) 
{ 
	return btree_level; 
} 

int count(btree t) 
{ 
	return btree_count; 
} 
double payload(btree t) 
{ 
	if (node_sum==0) 
		return 1; 
	return (double)btree_count/(node_sum*(2*M)); 
} 
btree deltree (btree t) 
{     
	level=btree_level; 
	btree_level = 0; 
	return delall(t); 

} 
btree delall(btree t) 
{ 
	int i; 
	level--; 
	if (level >= 0) { 
		for (i=0; i < t->d; i++) 
			if (t->v[ i ] != NULL) 
				free(t->v[ i ]); 
		if (level > 0) 
			for (i=0; i<= t->d ; i++) 
				t->p[ i ]=delall(t->p[ i ]); 
		free(t); 
	} 
	return NULL; 
} 

/* end of btrees.c */ 


void out_txt(int * pt,int n)
{
	FILE *fp_b;

	int i;


	if((fp_b=fopen("btree2.txt","a"))==NULL)
	{
		printf("cannot open this file\n");
		exit(0);
	}

	//fprintf(fp_b,"%d\t",bucket_volumes);
	for (i=0;i<n;i++)
	{
		fprintf(fp_b,"%d\t",pt[i]);
	}
	fprintf(fp_b,"\r");

}


int *read_txt(int n)
{

	FILE *fp;
	int i;
	int *a=(int *)malloc(n*sizeof(int));
	if ((fp = fopen("F:\\text_data.txt","rt")) == NULL)
	{
		printf("open file failed!\n");
		exit(1);
	}
	for (i=0; i<n; i++)
	{
		fscanf(fp,"%d", &a[i]);
		//              fprintf(fp,"%d ",a[i]);
	}
	fclose(fp);
	return a;
}
#include "btrees.h"
#define key_num 1000
void main()
{




	int * key_array;
	int i;
int pt[5];
	btree root,temp;
long long sf1,sf2,sf3;
LARGE_INTEGER f1,f2,f3,f5,f4,f6;

InsValue = NULL; /* 與要插的鍵相對應的值 */ 
 /* 節點增減標誌 */ 
btree_level = 0; /* 多路樹的高度 */ 
btree_count = 0; /* 多路樹的鍵總數 */ 
node_sum = 0;  /* 多路樹的節點總數 */ 

key_array=read_txt(key_num);
	temp = (btree)malloc(sizeof(node)); 
	temp->d = 0; 
	temp->p[0] = NewTree; 
	temp->p[1] = NewTree; 
	temp->k[0] = InsKey; 
	temp->v[0] = InsValue; 

	root=temp;




QueryPerformanceCounter(&f1);
	for (i=0;i<key_num;i++)
	{
		root=insert(key_array[i],root);

	}

QueryPerformanceCounter(&f2);
QueryPerformanceCounter(&f3);

		for (i=0;i<key_num;i++)
		{

		search(key_array[i],root);


			

		}

QueryPerformanceCounter(&f4);
QueryPerformanceCounter(&f5);
		for (i=0;i<key_num;i++)
		{

			root=delete(key_array[i],root);
			
		}

QueryPerformanceCounter(&f6);


sf1=f2.QuadPart-f1.QuadPart-750;
sf2=f4.QuadPart-f3.QuadPart-750;
sf3=f6.QuadPart-f5.QuadPart-750;