B树的实现(不带删除)

本文介绍了B-树的查找方法,通过根节点进行顺序或二分查找,未找到则递归在子节点中继续,直至找到关键字或遇到空指针。
摘要由CSDN通过智能技术生成
 

在B-树中查找给定关键字的方法是,首先把根结点取来,在根结点所包含的关键字K1,…,kj查找给定的关键字(可用顺序查找或二分查找法),若找到等于给定值的关键字,则查找成功;否则,一定可以确定要查的关键字在某个Ki或Ki+1之间,于是取Pi所指的结点继续查找,直到找到,或指针Pi为空时查找失败

 

 

代码:

 

头文件:BTree.h

 

#ifndef BTREE_H
#define BTREE_H
#define M 3							//B树的阶,暂设为3
#define N 17						//数据元素的个数

typedef struct BTNode{
	int keynum;						//结点中关键字的个数,即结点的大小
	struct BTNode *parent;			//指向双亲结点
	struct Node{					//关键字向量
		int key;					//结点中的关键字
		struct BTNode *ptr;			//子树指针向量
		int recptr;					//记录指针向量
	}node[M + 1];					//key,recptr的0号单元未用
}BTNode,*BTree;						//B树结点和B树的类型
typedef struct{
	BTNode *pt;						//指向找到的结点
	int i;							// 1.....m,在所找到的结点中关键字序号
	int tag;						// 1:查找成功,0:查找失败
}Result;							//B树的查找结果类型

//在p->node[1]....p->node[keynum]中查找k所处的位置p->node[i].key <= k < p->node[i+1].key
int Search(BTree p,int k);

//在M阶B树T上查找关键字K,返回结果Result
//查找成功,tag = 1,pt指向包函关键字K的结点,i为K在结点中的位置
//查找失败,tag = 0,pt指向K应插入的结点,i...i+1之间插入K
Result SearchBTree(BTree T,int k);

//将key插入到*q所指向的结点的第i + 1(0位置为使用)个位置
void Insert(BTree *q,int i,int key,BTree ap);

//在M阶B树T上,在q所指向的节点第i与i + 1的中间插入key
//若引起结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树
void InsertBTree(BTree *T,int key,BTree q,int i);

//将结点q分裂成两个结点,前一半保留,后一半移入新生结点ap
void Split(BTree *q,BTree *ap);

//生成新的根结点T,原T和ap为子树指针
void NewRoot(BTree *T,int key,BTree ap);

//打印出当前结点c的第i个位置的key
void print(BTNode c,int i);
#endif //BTREE_H


实现文件 :BTree.cpp

#include "BTree.h"
#include <stdlib.h>
#include <stdio.h>

//在p->node[1]....p->node[keynum]中查找k所处的位置p->node[i].key <= k < p->node[i+1].key
int Search(BTree p,int k)
{
	int i = 0;
	int j;
	for(j = 1;j <= p->keynum;++j)
		if(k >= p->node[j].key) //如果k大于p所指向的结点的第j个位置的关键字,记录下j
			i = j;
	return i;
}

//在M阶B树T上查找关键字K,返回结果Result
//查找成功,tag = 1,pt指向包函关键字K的结点,i为K在结点中的位置
//查找失败,tag = 0,pt指向K应插入的结点,i...i+1之间插入K
Result SearchBTree(BTree T,int k)
{
	Result r; //查找的结果
	bool found = false; //是否查找到的标志,初始化为未查找到
	int i = 0; //用于记录位置,如查找成功则是k所在结点的第i个位置,
		   //查找失败则是k应该插入的结点的第i个位置
	BTree p = T; //初始化为待查找的结点
	BTree q = NULL; //记录父结点,初始化为NULL
	while(p && !found) //待查找的结点不为NULL,且未找到进行循环
	{
		i = Search(p,k); //进行查找
		if(i > 0 && p->node[i].key == k) //如果查找成功
			found = true;
		else
		{
			q = p; //记录下当前结点,此结点是p->node[i].ptr所指结点的父结点
			p = p->node[i].ptr; //p赋值为它的子树,继续进行循环
		}
	}
	r.i = i; //如查找成功则是k所在结点的第i个位置
			 //查找失败则是k应该插入的结点的第i个位置
	if(found) //如果查找成功
	{
		r.pt = p;  //记录下所在的结点的指针
		r.tag = 1; //标记为查找成功
	}
	else{
		r.pt = q; //记录下要插入的结点
		r.tag = 0; //标记为查找不成功
	}
	return r;
}

//将key插入到*q所指向的结点的第i + 1个位置
void Insert(BTree *q,int i,int key,BTree ap)
{
	int j;
	for(j = (*q)->keynum;j > i;--j) //静态数组的插入,第i个位置之后的所有元素向后移一位
		(*q)->node[j + 1] = (*q)->node[j];
	(*q)->node[i + 1].key = key;
	(*q)->node[i + 1].ptr = ap;
	(*q)->node[i + 1].recptr = key;
	(*q)->keynum++;
}

//在M阶B树T上,在q所指向的节点第i与i + 1的中间插入key
//若引起结点过大,则沿双亲链进行必要的结点分裂调整,使T仍是m阶B树
void InsertBTree(BTree *T,int key,BTree q,int i)
{
	BTree ap = NULL;
	int s;
	int rx = key; //如果进行分裂,rx记录的是需要插入父结点的值
	bool finished = false; //记录插入是否完成,初始化为未完成
	while(q && !finished) //要插入的结点不为NULL,且插入未完成,进行循环
	{
		Insert(&q,i,rx,ap);
		if(q->keynum < M) //插入完成
			finished = true;
		else{	//超过BTree的阶,进行分裂 
			s = (M + 1) / 2;
			rx = q->node[s].recptr;
			Split(&q,&ap);
			q = q->parent;
			if(q)
				i = Search(q,key);  //查找key应该在父结点中插入的位置,在该位置插入rx
		}
	}
	if(!finished) //如果树是空树或已经分裂到根结点
		NewRoot(T,rx,ap); //生成新结点原来的T 和 ap做为子树
}

//将结点q分裂成两个结点,前一半保留,后一半移入新生结点ap
void Split(BTree *q,BTree *ap)
{
	*ap = (BTree)malloc(sizeof(BTNode));
	int s = (M + 1) / 2;
	(*ap)->node[0].ptr = (*q)->node[s].ptr;
	for(int 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;
	}
	(*ap)->parent = (*q)->parent;
	(*ap)->keynum = M - s;
	(*q)->keynum = s - 1; //q的前一半保留,修改keynum
}

//生成新的根结点T,原T和ap为子树指针
void NewRoot(BTree *T,int key,BTree ap)
{
	BTree temp = (BTree)malloc(sizeof(BTNode)); //新结点
	temp->node[0].ptr = *T; 
	*T = temp;
	if((*T)->node[0].ptr)
		(*T)->node[0].ptr->parent = *T;
	(*T)->parent = NULL;
	(*T)->keynum = 1;
	(*T)->node[1].key = key;
	(*T)->node[1].ptr = ap;
	(*T)->node[1].recptr = key;
	if((*T)->node[1].ptr)
		(*T)->node[1].ptr->parent = (*T);
}

//打印出当前结点c的第i个位置的key
void print(BTNode c,int i)
{
	printf("(%d)\n",c.node[i].key);
}


测试文件:main.cpp

#include "BTree.h"
#include <stdio.h>
int main()
{
	int a[N] = {11,5,15,18,17,22,59,16,29,1,68,55,99,13,19,62,57};
	Result r;
	BTree T = NULL;
	for(int i = 0;i < N;++i)
	{
		r = SearchBTree(T,a[i]);
		if(!r.tag)
			InsertBTree(&T,a[i],r.pt,r.i);
	}
	int found;
	printf("\n请输入要查找的数字:\n");
	scanf("%d",&found);
	r = SearchBTree(T,found);
	if(r.tag)
		print(*(r.pt),r.i);
	else
		printf("未找到\n");
	return 0;
}


 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值