B树的代码实现和解析
1.问题描述:
(1)实现在B-树上的查找,并分析其时间复杂性。
(2)实现B-树的ADT,包括其上的基本操作:结点的加入和删除。
(3)要求B-树结构中的M=3或5,实现其中的一种即可。
(4)实现基本操作的演示。
2.模块划分
(1)初始化B树。
(2)查找操作。
(3)显示操作。
(4)插入操作
(5)删除操作。
(6)销毁操作。
3.代码部分
#ifndef _BTREE_H
#define _BTREE_H
#define MAXM 10 //定义B树的最大的阶数const int m=5; //设定B树的阶数
const int Max=m-1; //结点的最大关键字数量
const int Min=(m-1)/2; //结点的最小关键字数量
typedef int KeyType; //KeyType为关键字类型typedef struct node{ //B树和B树结点类型 int keynum; //结点关键字个数KeyType key[MAXM]; //关键字数组,key[0]不使用 struct node *parent; //双亲结点指针struct node *ptr[MAXM]; //孩子结点指针数组
}BTNode,*BTree;typedef struct{ //B树查找结果类型 BTNode *pt; //指向找到的结点int i; //在结点中的关键字位置; int tag; //查找成功与否标志
}Result;typedef struct LNode{ //链表和链表结点类型 BTree data; //数据域struct LNode *next; //指针域
}LNode, *LinkList;typedef enum status{ //枚举类型(依次递增) TRUE,FALSE,OK,ERROR,OVERFLOW,EMPTY
}Status;Status InitBTree(BTree &t); //初始化B树
int SearchBTNode(BTNode *p,KeyType k); //在结点p中查找关键字k的插入位置i
Result SearchBTree(BTree t,KeyType k); /*在树t上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值tag=1,关键字k是指针pt所指结点中第i个关键字;否则特征值tag=0,关键字k的插入位置为pt结点的第i个*/
void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q); //将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中
void SplitBTNode(BTNode *&p,BTNode *&q); //将结点p分裂成两个结点,前一半保留,后一半移入结点q
void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q); //生成新的根结点t,原结点p和结点q为子树指针
void InsertBTree(BTree &t,int i,KeyType k,BTNode *p); /*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/
void Remove(BTNode *p,int i); //从p结点删除key[i]和它的孩子指针ptr[i]
void Substitution(BTNode *p,int i); //查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字)
void MoveRight(BTNode *p,int i); /*将双亲结点p中的最后一个关键字移入右结点q中将左结点aq中的最后一个关键字移入双亲结点p中*/
void MoveLeft(BTNode *p,int i); /*将双亲结点p中的第一个关键字移入结点aq中,将结点q中的第一个关键字移入双亲结点p中*/
void Combine(BTNode *p,int i); /*将双亲结点p、右结点q合并入左结点aq,并调整双亲结点p中的剩余关键字的位置*/
void AdjustBTree(BTNode *p,int i); //删除结点p中的第i个关键字后,调整B树
int FindBTNode(BTNode *p,KeyType k,int &i); //反映是否在结点p中是否查找到关键字k
int BTNodeDelete(BTNode *p,KeyType k); //在结点p中查找并删除关键字k
void BTreeDelete(BTree &t,KeyType k); //构建删除框架,执行删除操作
void DestroyBTree(BTree &t); //递归释放B树
Status InitQueue(LinkList &L); //初始化队列
LNode* CreateNode(BTree t); //新建一个结点
Status Enqueue(LNode *p,BTree t); //元素q入队列
Status Dequeue(LNode *p,BTNode *&q); //出队列,并以q返回值
Status IfEmpty(LinkList L); //队列判空
void DestroyQueue(LinkList L); //销毁队列
Status Traverse(BTree t,LinkList L,int newline,int sum); //用队列遍历输出B树
Status PrintBTree(BTree t); //输出B树
void Test(); //测试B树功能函数
#endif #include <stdio.h>
#include <malloc.h>
#include <stdlib.h> Status InitBTree(BTree &t){
//初始化B树 t=NULL;return OK;
}int SearchBTNode(BTNode *p,KeyType k){
//在结点p中查找关键字k的插入位置i int i=0;for(i=0;i<p->keynum&&p->key[i+1]<=k;i++);return i;
}Result SearchBTree(BTree t,KeyType k){
/*在树t上查找关键字k,返回结果(pt,i,tag)。若查找成功,则特征值
tag=1,关键字k是指针pt所指结点中第i个关键字;否则特征值tag=0,
关键字k的插入位置为pt结点的第i个*/BTNode *p=t,*q=NULL; //初始化结点p和结点q,p指向待查结点,q指向p的双亲int found_tag=0; //设定查找成功与否标志 int i=0; Result r; //设定返回的查找结果 while(p!=NULL&&found_tag==0){i=SearchBTNode(p,k); //在结点p中查找关键字k,使得p->key[i]<=k<p->key[i+1]if(i>0&&p->key[i]==k) //找到待查关键字found_tag=1; //查找成功 else{ //查找失败 q=p; p=p->ptr[i];}}if(found_tag==1){ //查找成功r.pt=p;r.i=i;r.tag=1;}else{ //查找失败r.pt=q;r.i=i;r.tag=0;}return r; //返回关键字k的位置(或插入位置)
}void InsertBTNode(BTNode *&p,int i,KeyType k,BTNode *q){
//将关键字k和结点q分别插入到p->key[i+1]和p->ptr[i+1]中int j;for(j=p->keynum;j>i;j--){ //整体后移空出一个位置p->key[j+1]=p->key[j];p->ptr[j+1]=p->ptr[j];}p->key[i+1]=k;p->ptr[i+1]=q;if(q!=NULL) q->parent=p;p->keynum++;
}void SplitBTNode(BTNode *&p,BTNode *&q){
//将结点p分裂成两个结点,前一半保留,后一半移入结点qint i;int s=(m+1)/2;q=(BTNode *)malloc(sizeof(BTNode)); //给结点q分配空间q->ptr[0]=p->ptr[s]; //后一半移入结点qfor(i=s+1;i<=m;i++){q->key[i-s]=p->key[i];q->ptr[i-s]=p->ptr[i];}q->keynum=p->keynum-s; q->parent=p->parent;for(i=0;i<=p->keynum-s;i++) //修改双亲指针 if(q->ptr[i]!=NULL) q->ptr[i]->parent=q;p->keynum=s-1; //结点p的前一半保留,修改结点p的keynum
}void NewRoot(BTNode *&t,KeyType k,BTNode *p,BTNode *q){
//生成新的根结点t,原p和q为子树指针t=(BTNode *)malloc(sizeof(BTNode)); //分配空间 t->keynum=1;t->ptr[0]=p;t->ptr[1]=q;t->key[1]=k;if(p!=NULL) //调整结点p和结点q的双亲指针 p->parent=t;if(q!=NULL) q->parent=t;t->parent=NULL;
}void InsertBTree(BTree &t,int i,KeyType k,BTNode *p){
/*在树t上结点q的key[i]与key[i+1]之间插入关键字k。若引起
结点过大,则沿双亲链进行必要的结点分裂调整,使t仍是B树*/BTNode *q;int finish_tag,newroot_tag,s; //设定需要新结点标志和插入完成标志 KeyType x;if(p==NULL) //t是空树NewRoot(t,k,NULL,NULL); //生成仅含关键字k的根结点telse{x=k;q=NULL;finish_tag=0; newroot_tag=0;while(finish_tag==0&&newroot_tag==0){InsertBTNode(p,i,x,q); //将关键字x和结点q分别插入到p->key[i+1]和p->ptr[i+1]if (p->keynum<=Max) finish_tag=1; //插入完成else{ s=(m+1)/2;SplitBTNode(p,q); //分裂结点 x=p->key[s];if(p->parent){ //查找x的插入位置p=p->parent;i=SearchBTNode(p, x);}else //没找到x,需要新结点 newroot_tag=1;}}if(newroot_tag==1) //根结点已分裂为结点p和q NewRoot(t,x,p,q); //生成新根结点t,p和q为子树指针}
}void Remove(BTNode *p,int i){
//从p结点删除key[i]和它的孩子指针ptr[i]int j;for(j=i+1;j<=p->keynum;j++){ //前移删除key[i]和ptr[i]p->key[j-1]=p->key[j];p->ptr[j-1]=p->ptr[j];}p->keynum--;
}void Substitution(BTNode *p,int i){
//查找被删关键字p->key[i](在非叶子结点中)的替代叶子结点(右子树中值最小的关键字) BTNode *q;for(q=p->ptr[i];q->ptr[0]!=NULL;q=q->ptr[0]);p->key[i]=q->key[1]; //复制关键字值
}void MoveRight(BTNode *p,int i){
/*将双亲结点p中的最后一个关键字移入右结点q中
将左结点aq中的最后一个关键字移入双亲结点p中*/ int j;BTNode *q=p->ptr[i];BTNode *aq=p->ptr[i-1];for(j=q->keynum;j>0;j--){ //将右兄弟q中所有关键字向后移动一位q->key[j+1]=q->key[j];q->ptr[j+1]=q->ptr[j];}q->ptr[1]=q->ptr[0]; //从双亲结点p移动关键字到右兄弟q中q->key[1]=p->key[i];q->keynum++;p->key[i]=aq->key[aq->keynum]; //将左兄弟aq中最后一个关键字移动到双亲结点p中p->ptr[i]->ptr[0]=aq->ptr[aq->keynum];aq->keynum--;
}void MoveLeft(BTNode *p,int i){
/*将双亲结点p中的第一个关键字移入左结点aq中,
将右结点q中的第一个关键字移入双亲结点p中*/ int j;BTNode *aq=p->ptr[i-1];BTNode *q=p->ptr[i];aq->keynum++; //把双亲结点p中的关键字移动到左兄弟aq中aq->key[aq->keynum]=p->key[i]; aq->ptr[aq->keynum]=p->ptr[i]->ptr[0];p->key[i]=q->key[1]; //把右兄弟q中的关键字移动到双亲节点p中q->ptr[0]=q->ptr[1];q->keynum--;for(j=1;j<=aq->keynum;j++){ //将右兄弟q中所有关键字向前移动一位aq->key[j]=aq->key[j+1];aq->ptr[j]=aq->ptr[j+1];}
}void Combine(BTNode *p,int i){
/*将双亲结点p、右结点q合并入左结点aq,
并调整双亲结点p中的剩余关键字的位置*/ int j;BTNode *q=p->ptr[i]; BTNode *aq=p->ptr[i-1];aq->keynum++; //将双亲结点的关键字p->key[i]插入到左结点aq aq->key[aq->keynum]=p->key[i];aq->ptr[aq->keynum]=q->ptr[0];for(j=1;j<=q->keynum;j++){ //将右结点q中的所有关键字插入到左结点aq aq->keynum++;aq->key[aq->keynum]=q->key[j];aq->ptr[aq->keynum]=q->ptr[j];}for(j=i;j<p->keynum;j++){ //将双亲结点p中的p->key[i]后的所有关键字向前移动一位 p->key[j]=p->key[j+1];p->ptr[j]=p->ptr[j+1];}p->keynum--; //修改双亲结点p的keynum值 free(q); //释放空右结点q的空间
}void AdjustBTree(BTNode *p,int i){
//删除结点p中的第i个关键字后,调整B树if(i==0) //删除的是最左边关键字if(p->ptr[1]->keynum>Min) //右结点可以借MoveLeft(p,1);else //右兄弟不够借 Combine(p,1);else if(i==p->keynum) //删除的是最右边关键字if(p->ptr[i-1]->keynum>Min) //左结点可以借 MoveRight(p,i);else //左结点不够借 Combine(p,i);else if(p->ptr[i-1]->keynum>Min) //删除关键字在中部且左结点够借 MoveRight(p,i);else if(p->ptr[i+1]->keynum>Min) //删除关键字在中部且右结点够借 MoveLeft(p,i+1);else //删除关键字在中部且左右结点都不够借Combine(p,i);
}int FindBTNode(BTNode *p,KeyType k,int &i){
//反映是否在结点p中是否查找到关键字k if(k<p->key[1]){ //结点p中查找关键字k失败 i=0;return 0;}else{ //在p结点中查找i=p->keynum;while(k<p->key[i]&&i>1)i--;if(k==p->key[i]) //结点p中查找关键字k成功 return 1;}
}int BTNodeDelete(BTNode *p,KeyType k){
//在结点p中查找并删除关键字kint i;int found_tag; //查找标志 if(p==NULL) return 0;else{found_tag=FindBTNode(p,k,i); //返回查找结果 if(found_tag==1){ //查找成功 if(p->ptr[i-1]!=NULL){ //删除的是非叶子结点Substitution(p,i); //寻找相邻关键字(右子树中最小的关键字) BTNodeDelete(p->ptr[i],p->key[i]); //执行删除操作 }elseRemove(p,i); //从结点p中位置i处删除关键字}elsefound_tag=BTNodeDelete(p->ptr[i],k); //沿孩子结点递归查找并删除关键字kif(p->ptr[i]!=NULL)if(p->ptr[i]->keynum<Min) //删除后关键字个数小于MINAdjustBTree(p,i); //调整B树 return found_tag;}
}void BTreeDelete(BTree &t,KeyType k){
//构建删除框架,执行删除操作 BTNode *p;int a=BTNodeDelete(t,k); //删除关键字k if(a==0) //查找失败 printf(" 关键字%d不在B树中\n",k);else if(t->keynum==0){ //调整 p=t;t=t->ptr[0];free(p);}
}void DestroyBTree(BTree &t){
//递归释放B树 int i; BTNode* p=t; if(p!=NULL){ //B树不为空 for(i=0;i<=p->keynum;i++){ //递归释放每一个结点 DestroyBTree(*&p->ptr[i]); } free(p); } t=NULL;
} Status InitQueue(LinkList &L){
//初始化队列 L=(LNode*)malloc(sizeof(LNode)); //分配结点空间 if(L==NULL) //分配失败 return OVERFLOW;L->next=NULL;return OK;
}LNode* CreateNode(BTNode *p){
//新建一个结点 LNode *q;q=(LNode*)malloc(sizeof(LNode)); //分配结点空间if(q!=NULL){ //分配成功 q->data=p;q->next=NULL;}return q;
}Status Enqueue(LNode *p,BTNode *q){
//元素q入队列if(p==NULL) return ERROR; while(p->next!=NULL) //调至队列最后 p=p->next;p->next=CreateNode(q); //生成结点让q进入队列 return OK;
}Status Dequeue(LNode *p,BTNode *&q){
//出队列,并以q返回值 LNode *aq;if(p==NULL||p->next==NULL) //删除位置不合理 return ERROR; aq=p->next; //修改被删结点aq的指针域p->next=aq->next; q=aq->data;free(aq); //释放结点aqreturn OK;
}Status IfEmpty(LinkList L){
//队列判空 if(L==NULL) //队列不存在 return ERROR;if(L->next==NULL) //队列为空 return TRUE;return FALSE; //队列非空
}void DestroyQueue(LinkList L){
//销毁队列 LinkList p;if(L!=NULL){p=L;L=L->next;free(p); //逐一释放 DestroyQueue(L);}
}Status Traverse(BTree t,LinkList L,int newline,int sum){
//用队列遍历输出B树 int i;BTree p;if(t!=NULL){printf(" [ ");Enqueue(L,t->ptr[0]); //入队 for(i=1;i<=t->keynum;i++){printf(" %d ",t->key[i]);Enqueue(L,t->ptr[i]); //子结点入队 }sum+=t->keynum+1;printf("]");if(newline==0){ //需要另起一行 printf("\n");newline=sum-1;sum=0;}elsenewline--;}if(IfEmpty(L)==FALSE){ //l不为空 Dequeue(L,p); //出队,以p返回 Traverse(p,L,newline,sum); //遍历出队结点 }return OK;}Status PrintBTree(BTree t){
//输出B树 LinkList L;if(t==NULL){printf(" B树为空树");return OK;}InitQueue(L); //初始化队列 Traverse(t,L,0,0); //利用队列输出 DestroyQueue(L); //销毁队列 return OK;
}void Test1(){ system("color 70"); BTNode *t=NULL;Result s; //设定查找结果 int j,n=15;KeyType k;KeyType a[]={1,2,3,4,5,6,7,8,9,10,11,12,13,14,15}; printf("创建一棵%d阶B树:\n",m);for(j=0;j<n;j++){ //逐一插入元素 s=SearchBTree(t,a[j]);if(s.tag==0)InsertBTree(t,s.i,a[j],s.pt);printf(" 第%d步,插入元素%d:\n ",j+1,a[j]);PrintBTree(t);}printf("\n");printf("删除操作:\n"); //删除操作 k=9; BTreeDelete(t,k);printf(" 删除%d:\n ",k);printf(" 删除后的B树: \n");PrintBTree(t);printf("\n");k=1;BTreeDelete(t,k);printf(" 删除%d:\n ",k);printf(" 删除后的B树: \n");PrintBTree(t);printf("\n");printf(" 递归释放B树\n"); //递归释放B树DestroyBTree(t); PrintBTree(t);
} void Test2(){int i,k; system("color 70");BTree t=NULL; Result s; //设定查找结果 while(1){printf("此时的B树:\n");PrintBTree(t); printf("\n");printf("=============Operation Table=============\n");printf(" 1.Init 2.Insert 3.Delete \n");printf(" 4.Destroy 5.Exit \n");printf("=========================================\n");printf("Enter number to choose operation:_____\b\b\b");scanf("%d",&i);switch(i){case 1:{InitBTree(t);printf("InitBTree successfully.\n");break;}case 2:{printf("Enter number to InsertBTree:_____\b\b\b");scanf("%d",&k);s=SearchBTree(t,k);InsertBTree(t,s.i,k,s.pt);printf("InsertBTree successfully.\n");break;}case 3:{printf("Enter number to DeleteBTree:_____\b\b\b");scanf("%d",&k);BTreeDelete(t,k);printf("\n");printf("DeleteBTree successfully.\n");break;}case 4:{DestroyBTree(t);break;printf("DestroyBTree successfully.\n");}case 5:{exit(-1); break;}}}
}int main(){Test2();return 0;
}
B树的代码实现和解析相关推荐
- JavaScript 语法树与代码转化实践
JavaScript 语法树与代码转化实践 归纳于笔者的现代 JavaScript 开发:语法基础与实践技巧系列文章中.本文引用的参考资料声明于 JavaScript 学习与实践资料索引中,特别需要声 ...
- 数据结构与算法——24. 树的应用:表达式解析树
文章目录 一.解析树 二.解析树实例:表达式解析 1. 建立表达式解析树 (1)建立表达式解析树的规则 (2)建立表达式解析树的思路 (3)python代码实现 2. 表达式解析树的求值 (1)增加程 ...
- 数据结构与算法(Python版)四十八:树的应用(表达式解析)
树的应用:解析树(语法树) 将树用于表示语言中句子, 可以分析句子的各种语法成分, 对句子的各种成分进行处理 语法分析树主谓宾,定状补 程序设计语言的编译 词法.语法检查从语法树生成目标代码 自然语言 ...
- 04.南瓜树低代码平台平台 分析后的感想
随着企业产品的不断完善,后续将有时间来推进产品转向低代码平台化. 低代码平台不是无代码平台,采用配置的方式完成UI/流程/报表的处理,有业务人员在完成基本的产品框架后,由研发人员完成业务规则代码固化, ...
- 【机器学习】树回归和聚类算法解析和应用
[机器学习]树回归和聚类算法解析和应用 文章目录 1 树回归 2 CART ( Classification And Regression Tree) 分类回归树 3 K-means3.1 合理选择 ...
- Android代码入侵原理解析(一)
Original 2017-05-06 付超红 滴滴安全应急响应中心 2017年初,在滴滴安全沙龙上,滴滴出行安全专家--付超红,针对App的攻与防进行了分享.会后大家对这个议题反响热烈,纷纷求详情求 ...
- python中文词云图代码_Python简单实现词云图代码及步骤解析
一.安装 wordcloud pip install wordcloud 二.加载包.设置路径 import os from wordcloud import WordCloud import mat ...
- java解析shell命令_Android中执行java命令的方法及java代码执行并解析shell命令
这篇文章给大家介绍Android中执行java命令的方法及java代码执行并解析shell命令,需要的朋友一起学习 android中执行java命令的方法大家都晓得吗,下面一段内容给大家带来了具体解析 ...
- 如何在vs中创建r树索引代码_线段树详解与实现
此篇文章用于记录<玩转数据结构>课程的学习笔记 什么是线段树 线段树也被称为区间树,英文名为Segment Tree或者Interval tree,是一种高级的数据结构.这种数据结构更多出 ...
最新文章
- Java设计模式(访问者模式-迭代器模式-观察者模式-中介者模式)
- UVA 12063 Zeros and Ones
- BZOJ3261 最大异或和 解题报告(可持久化Trie树)
- 深度学习的非主流应用
- 求【javascript设计模式】【高性能网站建设指南】PDF!哪位有给下啊!!!
- shell将命令执行的结果赋值给 变量
- set、vector与list的构造与排序的耗时测试
- SpringCloudStream整合rabbitMq
- 我们的高中教育造就了什么?
- PHP-电脑搭建服务器
- 星巴克——最单纯的SNS应用
- 1.MySql驱动的jar包下载
- js实现商城特效---鼠标移入图片放大
- 三国志战略版:当锋无法破防的司马盾
- 6-1 哈夫曼树及哈夫曼编码
- 【天下有春】剑气纵横三万里,一剑光寒十九洲
- 用开源github,还是咱中国自己的代码托管平台云效?
- Enhancing Adversarial Training with Second-Order Statistics of Weights
- 【Unity】Post-process后处理之Grain
- 大师兄科研网_怎样知道一名研究生有没有科研潜力?
热门文章
- linux 内核 虚拟地址,linux内核中的虚拟到物理地址转换
- storm显微镜成像原理_STORM 超分辨显微成像 – Prime 95B 应用
- 360剑灵洪门崛起服务器维护,剑灵洪门崛起————【维护】9月12日更新维护公告...
- 00后女生“云摆摊”两周赚1.5万,实体店转战线上真的能赚钱吗?
- c# 隐藏显示 任务栏
- MSP430学习总结(二)——GPIO
- 小程序客服聊天发送商品详情,快捷发送链接和图文消息,附代码和流程
- 文本文件和二进制文件的定义与区别
- pytorch测试模型时显存不够的问题
- 快吧我的世界盒子java怎么_快吧我的世界盒子怎么解除维护,快吧我的世界盒子解除维护教程...