删除算法
设计一个算法在一棵B树上删除关键码k。若B树中没有等于k的关键码,则不删除。
【解答】:想要在B树上删除一个关键码,首先需要找到这个关键码所在的结点,若该节点不是叶节点,且被删关键码为 K i K_i Ki,1<=i<=n,则在删除关键码之后,应以该节点 p I p_I pI所指向子树中的最小关键码x来代替被删关键码 K i K_i Ki所在的位置,然后在x所在的叶结点中删除x,在叶结点上的删除有4种情况:
- 若该叶结点1又是根结点时,在删除前该节点中关键码个数n>=2,则直接删去关键码即可,若n<2则删除后b树变空,删除结束。
若该节点不是根节点,则 - 若删除前该节点关键码个数n>=[m/2],则直接删去该关键码,删除结束。
- 若删除前该节点关键码个数n=[m/2]-1,若此时右兄弟(左兄弟) 结点的关键码个数n>=[m/2],则可进行过关键码移动,以达到新的平衡.
- 若删除前结点关键码个数n = [m/2]-1,此时右兄弟(或左兄弟)结点的关键码个数n=[m/2]-1,则可进行结点合并,以达到新的平衡
void merge(BTNode *p,BTNode *pr,BTNode *q,int i ){
/* *p 是*pr的第i个子女,算法让*p让其右兄弟*q合并,保留*p结点
* 双亲*pr 的key[i+1]下落到*p
* */
p->key[(p->n)+1] = pr->key[i+1]; //从双亲*pr 下降关键码key[i+1]
p->ptr[(p->n)+1] = q->ptr[0]; //从右兄弟*q左移指针q->str[0]
if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
for(int k = 1;k<=q->n;k++) {
//右结点其他信息左移
p->key[(p->n)+k+1] = q->key[k];
p->ptr[(p->n)+k+1] = q->ptr[k];
if(q->ptr[k] !=NULL )q->ptr[k] ->parent = p;
}
p->n = (p->n)+(q->n) + 1; //修改*p 中关键码个数
free(q); //释放*q
for(int k = i+2;k<=pr->n;k++) //双亲结点*pr压缩
{
pr->key[k-1] = pr->key[k];
pr->ptr[k-1] = pr->ptr[k];
}
pr->n--;
}
bool LeftAdjust(BTNode *p,BTNode *pr,int d,int j){
//*p是其双亲*pr 的第j个子女,*p与*pr,右兄弟*q一起调整
BTNode *q = pr->ptr[j+1];
int k; //*p的右兄弟
if(q->n > d-1){
//右兄弟空间够,做移动
p->key[p->n+1] = pr->key[j+1];//双亲结点相应关键码下移
pr->key[j+1] = q->key[1]; //右兄弟关键码上移
p->ptr[p->n+1] = q->ptr[0]; //右兄弟最左指针左移
if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
for(k=1;k<=q->n;k++) q->ptr[k-1] = q->ptr[k];
for(k=2;k<=q->n;k++) q->key[k-1] = q->key[k];
p->n++;
q->n--;
return false;
}else{
merge(p,pr,q,j);
return true; //p与q合并
}
}
bool RightAdjust(BTNode *p,BTNode *pr,int d,int j){
//*p是其双亲*pr 的第j个子女,*p与*pr,左兄弟*q一起调整
BTNode *q = pr->ptr[j-1];
int k; //*p的左兄弟
if(q->n > d-1){
//左兄弟空间够,做移动
for(k=p->n;k>=0;k--) p->ptr[k+1] = p->ptr[k];
for(k=p->n;k>=1;k--) p->key[k+1] = p->key[k];
p->ptr[0] = q->ptr[q->n];//左兄弟最后指针右移
if(q->ptr[q->n] != NULL) q->ptr[q->n]->parent = p;
p->key[1] = pr->key[j]; //双亲相应关键码下移
pr->key[j] = q->key[q->n];//左兄弟最后关键码上衣移
q->n--;
p->n++;
return false;
}else{
merge(q,pr,p,j-1);
return true; //p与q合并
}
}
bool Remove(BTree &T,KeyType k){
//算法调用方式bool succ = remove(T,k)
//输入B树的根指针t,删除关键码k
//成功true,错误false
int i,j,d = (m+1)/2;
BTNode *p,*pr,*s;
bool succ;
if(!Search(T,k,p,i)) return false; //查找k失败,返回
if(p->ptr[i]!= NULL){
//若p是非叶节点
s = p->ptr[i];
pr = p; //查找K[i]的右子树左下节点pr
while(s!=NULL) {
pr = s;
s = s->ptr[0];
}
p->key[i] = pr->key[1];//用此结点最小关键码寻找
for(j=2;j<=pr->n;j++){
pr->key[j-1] = pr->key[j];
pr->ptr[j-1] = pr->ptr[j];
}
pr->n--; //叶结点关键码个数减1
p = pr; //下一步处理q结点中的删除
}else{
for(j = i+1;j<=p->n;j++){
p->key[j-1] = p->key[j];
p->ptr[j-1] = p->ptr[j];
}
p->n--; //叶节点关键码个数减1
}
while(1){
if(p->n < d-1){
pr = p->parent;
for(j=0;j<=pr->n && pr->ptr[j] != p;j++);
if(j==0)
succ = LeftAdjust(p,pr,d,j);
else
succ = RightAdjust(p,pr,d,j);
if(succ){
//继续向上做节点调整工作
p = pr;
if(p==T) break;
}
}else break; //不小于d-1,无需调整,跳出
}
if(T->n == 0){
//当根节点为空删除节点
p = T->ptr[0];
free(T);
T = p;
T->parent = NULL;
}
return true;
}
统计B树关键码个数
采用深度优先就行了
void count_Keynum(BTree T,int& count){
if(T != NULL){
count = count+ T->n;
for(int i =0;i<=T->n;i++) //累加关键码个数
count_Keynum(T[i],count);//进入子树累加
}
}
遍历B树,从小到大打印树值
也是深度优先遍历,有一种先序遍历的感觉
void Traversal(BTree T){
if(T!=NULL){
Traversal(T->ptr[0]);
for(int i =1;i<=T->n;i++){
printf("%d ",T->key[i]);
Traversal(T->ptr[i]);
}
}
}
创建B树
用B树的插入算法Insert(T,k),从空树开始,输入一连串关键码a0,a1,…,创建一颗B树,约定输入结束标志是finish,这是一个特定的关键码。
void createBTree(BTree &T){
//createBTree(T),输入:B树根指针T;输出;逐个输入数据
KeyType a,finish = 0;
T = NULL;
scanf("%d",&a);
while(a!=finish){
Insert(T,a); //将输入的关键码插入到B树。
scanf("%d",&a);
}
}
完整源码
//完成P109算法设计题第(3)题的算法设计与实现,并编写程序进行验证
#include<iostream>
#define MaxValue 255
#define m 5 //b树的阶数
using namespace std;
typedef int KeyType;
typedef struct node{
int n; //结点内关键词个数
struct node*parent; //双亲结点指针
int pno; //双亲结点指针
KeyType key[m+1]; //key[m]为监视韶兼工作单元 key[0]未用
struct node*ptr[m+1]; //子树结点指针数组,ptr[m]在插入溢出时用
int *recptr[m+1]; //每个索引项指向数据区记录地址的指针
}BTNode,*BTree; //B树的定义
//对b树进行查找k
//b树的阶为m,最大高度h<= log[m/2]((n+1)/2)+1,最小高度为h>=logm(n+1) 查找树的比较次数不超过m*h
bool Search(BTree T,KeyType k,BTNode *&p,int &i){
BTNode *pre; //p来扫描,pre是双亲指针
p = T;
pre = NULL;
while(p!=NULL){
//从根开始检测
i = 0;
p->key[(p->n)+1] = MaxValue;
while(p->key[i+1] <k) i++;
if(p->key[i+1] == k) {
//在结点内顺序查找
i++;
return true; //查找成功,本节点有k
}
pre = p;
p = p->ptr[i]; //本节点无k下降到子树
}
p = pre;
i++;
return false;
}
//对b树进行查找
//m阶b树的高度为k,插入的算法比较次数不超过m*h,读写次数3h+1
bool Insert(BTree &T,KeyType k){
int i,j,s = (m+1)/2;
BTNode *p,*ap,*q;
if(T==NULL){
T = (BTNode *)malloc(sizeof(BTNode));
T->ptr[0] = T->ptr[1] = NULL;
T->key[1] = k;
T->parent = NULL;
T->n = 1;
return true;
}
if(Search(T,k,p,i)) return false;
ap = NULL;
while(1){
if(i> p->n) {
p->key[i] = k;
p->ptr[i] = ap;
}else{
for(j = p->n;j>=i;j--){
//空出结点p第i个位置
p->key[j+1] = p->key[j];
p->ptr[j+1] = p->ptr[j];
}
p->key[j+1] = k; //插入关键词k
p->ptr[j+1] = ap;
}
(p->n)++; //结点关键词个数+1
if(p->n == m){
//结点关键吗个数超出上限
q = (BTNode *)malloc(sizeof(BTNode)); //分裂,创建新结点q
q->ptr[0] = p->ptr[s]; //传送p的后半部分q
for(j = s+1;j<=m;j++){
q->key[j-s] = p->key[j];
q->ptr[j-s] = p->ptr[j];
}
p->n = s-1;
q->n = m-s;
q->parent = p->parent;
for(j=0;j<=q->n;j++)
if(q->ptr[j]!=NULL) q->ptr[j]->parent = q;
k= p->key[s]; //(k,ap)形成向上插入二元组
ap = q;
if(p->parent != NULL){
//分裂的不是根节点
p = p->parent; //转向双亲插入中间关键吗
for(i=1;i<=p->n&&p->key[i]<k;i++);
}else{
T = (BTNode *) malloc(sizeof(BTNode));
T->ptr[0] =p;
T->ptr[1] = ap;
T->key[1] = k;
p->parent = T;
ap->parent = T;
T->parent = NULL;
T->n = 1;
return true; //新根结点创建完,返回
}
}
else return true; //插入后结点不溢出,返回
}
}
//遍历
void preTraversal(BTree T,int k){
if(T !=NULL){
printf("level = %d,n=%d ",k,T->n);
for(int i =1;i<=T->n;i++)
printf(" %d ",T->key[i]);
printf("\n");
for(int i=0;i<=T->n;i++)
preTraversal(T->ptr[i],k+1); //递归遍历1第i课子树
}
}
void merge(BTNode *p,BTNode *pr,BTNode *q,int i ){
/* *p 是*pr的第i个子女,算法让*p让其右兄弟*q合并,保留*p结点
* 双亲*pr 的key[i+1]下落到*p
* */
p->key[(p->n)+1] = pr->key[i+1]; //从双亲*pr 下降关键码key[i+1]
p->ptr[(p->n)+1] = q->ptr[0]; //从右兄弟*q左移指针q->str[0]
if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
for(int k = 1;k<=q->n;k++) {
//右结点其他信息左移
p->key[(p->n)+k+1] = q->key[k];
p->ptr[(p->n)+k+1] = q->ptr[k];
if(q->ptr[k] !=NULL )q->ptr[k] ->parent = p;
}
p->n = (p->n)+(q->n) + 1; //修改*p 中关键码个数
free(q); //释放*q
for(int k = i+2;k<=pr->n;k++) //双亲结点*pr压缩
{
pr->key[k-1] = pr->key[k];
pr->ptr[k-1] = pr->ptr[k];
}
pr->n--;
}
bool LeftAdjust(BTNode *p,BTNode *pr,int d,int j){
//*p是其双亲*pr 的第j个子女,*p与*pr,右兄弟*q一起调整
BTNode *q = pr->ptr[j+1];
int k; //*p的右兄弟
if(q->n > d-1){
//右兄弟空间够,做移动
p->key[p->n+1] = pr->key[j+1];//双亲结点相应关键码下移
pr->key[j+1] = q->key[1]; //右兄弟关键码上移
p->ptr[p->n+1] = q->ptr[0]; //右兄弟最左指针左移
if(q->ptr[0] != NULL) q->ptr[0]->parent = p;
for(k=1;k<=q->n;k++) q->ptr[k-1] = q->ptr[k];
for(k=2;k<=q->n;k++) q->key[k-1] = q->key[k];
p->n++;
q->n--;
return false;
}else{
merge(p,pr,q,j);
return true; //p与q合并
}
}
bool RightAdjust(BTNode *p,BTNode *pr,int d,int j){
//*p是其双亲*pr 的第j个子女,*p与*pr,左兄弟*q一起调整
BTNode *q = pr->ptr[j-1];
int k; //*p的左兄弟
if(q->n > d-1){
//左兄弟空间够,做移动
for(k=p->n;k>=0;k--) p->ptr[k+1] = p->ptr[k];
for(k=p->n;k>=1;k--) p->key[k+1] = p->key[k];
p->ptr[0] = q->ptr[q->n];//左兄弟最后指针右移
if(q->ptr[q->n] != NULL) q->ptr[q->n]->parent = p;
p->key[1] = pr->key[j]; //双亲相应关键码下移
pr->key[j] = q->key[q->n];//左兄弟最后关键码上衣移
q->n--;
p->n++;
return false;
}else{
merge(q,pr,p,j-1);
return true; //p与q合并
}
}
bool Remove(BTree &T,KeyType k){
//算法调用方式bool succ = remove(T,k)
//输入B树的根指针t,删除关键码k
//成功true,错误false
int i,j,d = (m+1)/2;
BTNode *p,*pr,*s;
bool succ;
if(!Search(T,k,p,i)) return false; //查找k失败,返回
if(p->ptr[i]!= NULL){
//若p是非叶节点
s = p->ptr[i];
pr = p; //查找K[i]的右子树左下节点pr
while(s!=NULL) {
pr = s;
s = s->ptr[0];
}
p->key[i] = pr->key[1];//用此结点最小关键码寻找
for(j=2;j<=pr->n;j++){
pr->key[j-1] = pr->key[j];
pr->ptr[j-1] = pr->ptr[j];
}
pr->n--; //叶结点关键码个数减1
p = pr; //下一步处理q结点中的删除
}else{
for(j = i+1;j<=p->n;j++){
p->key[j-1] = p->key[j];
p->ptr[j-1] = p->ptr[j];
}
p->n--; //叶节点关键码个数减1
}
while(1){
if(p->n < d-1){
pr = p->parent;
for(j=0;j<=pr->n && pr->ptr[j] != p;j++);
if(j==0)
succ = LeftAdjust(p,pr,d,j);
else
succ = RightAdjust(p,pr,d,j);
if(succ){
//继续向上做节点调整工作
p = pr;
if(p==T) break;
}
}else break; //不小于d-1,无需调整,跳出
}
if(T->n == 0){
//当根节点为空删除节点
p = T->ptr[0];
free(T);
T = p;
T->parent = NULL;
}
return true;
}
void count_Keynum(BTree T,int& count){
if(T != NULL){
count = count+ T->n;
for(int i =0;i<=T->n;i++) //累加关键码个数
count_Keynum(T->ptr[i],count);//进入子树累加
}
}
void Traversal(BTree T){
if(T!=NULL){
Traversal(T->ptr[0]);
for(int i =1;i<=T->n;i++){
printf("%d ",T->key[i]);
Traversal(T->ptr[i]);
}
}
}
int main(){
BTree T=NULL;
int a[11]={
2,3,4,5,6,7,8,9,10,11,12};
// Insert(T,a[0]);
for(int i =0;i<11;i++){
Insert(T,a[i]);
}
Traversal(T);
return 0;
}