1. 程式人生 > >B-樹、B+樹、紅黑樹

B-樹、B+樹、紅黑樹

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