B-樹、B+樹、紅黑樹
阿新 • • 發佈:2018-12-25
#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;
}
#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);
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;
}