B-TREE

这里只用关键字来代替实际的外存文件。

B树又叫平衡多路查找树,是一种组织和维护外存文件系统非常有效的数据结构
一棵m阶的B树(m叉树)的特性如下:
树中每个结点最多含有m个孩子(m>=2);
除根结点和叶子结点外,其它每个结点至少有[ceil(m / 2)]=(m-1)/2个孩子(其中ceil(x)是一个取上限的函数);
若根结点不是叶子结点,则至少有2个孩子(特殊情况:没有孩子的根结点,即根结点为叶子结点,整棵树只有一个根节点);
所有叶子结点都出现在同一层,叶子结点不包含任何关键字信息(可以看做是外部接点或查询失败的接点,
实际上这些结点不存在,指向这些结点的指针都为null);
每个非终端结点中包含有n个关键字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
a)   Ki (i=1...n)为关键字,且关键字按顺序升序排序K(i-1)< Ki。 
b)   Pi为指向子树根的接点,且指针P(i-1)指向子树种所有结点的关键字均小于Ki,但都大于K(i-1)。 
c)   关键字的个数n必须满足: (m-1)/2<= n <= m-1


/*
* B树的插入算法
*将关键字K插入到B树中分两步进行:


(1)、利用前述的B树的查找算法找出该关键字的插入结点(插入结点一定是叶子结点)

(2)、判断该结点是否有空位置,即判断该结点是有满足n<m-1;若满足,则说明还有空位置

直接插入即可(要保持该结点的关键字仍然有序);

若n=m-1,说明该结点已经没有空位置,要将该结点分裂成两个.分裂的做法是,取一个

新结点,把原结点上的关键字和k按升序排列,从中间位置((m+1)/2处)把关键字(

不包含中间关键字)分裂成两部分,左部分所含关键字放在旧节点中,右部分所含关键

字放在新结点中,中间关键字连同新结点的地址插入到父节点中。

如果父节点的关键字数也满了,那就向上继续分裂,最多是做到把根节点也分裂,产生

新的根节点。
*/

/*
*B树的删除算法
*要使得删除后的结点满足n>=(m-1)/2,将涉及到结点的"合并"问题。
*在B树中删除关键字K的过程分两步完成:

(1)、查找出该关键字所在结点

(2)、在结点上删除关键字k分两种情况:一是在叶子结点上删除、二在非叶子结点上删除

1)、在非叶子结点上删除关键字k的过程:假设要删除的关键字key[i],在删去该关键

字后,以该结点的ptr[i]所指向的子树中的最小关键字key[min]来代替被删关键

字key[i]所在的位置(注意ptr[i]所指子树中的最小关键字key[min]一定是在叶子

节点上),然后再也指针ptr[i]所指结点为根节点查找并删除key[min](即以ptr[i]

所指的结点为B树的根节点,key[min]为删除结点,然后调用B树的删除算法),这样

也就把在非叶子结点上删除关键字k的问题转化为在叶子结点上面删除关键字key[min]的问题。

2)、在叶子结点上删除关键字共分以下三种情况:

NO.1:如果被删除的结点的关键字数大于(m-1)/2,说明删去该关键字后,该结点仍满
足B树的定义,可以直接删除.

NO.2:如果被删除的结点的关键字数等于(m-1)/2,说明删去该节点后关键字数将不满足
B树的定义,此时若该结点的左(或右)兄弟结点的关键字树大于(m-1)/2,则把该
结点的左(或右)兄弟结点中最大(或最小)的关键字上移到双亲结点中,同时把双
亲结点中大于(或小于)上移关键字的关键字下移到要删除的关键字结点中,这样
删去该关键字后,该结点的以及它的左(或右)兄弟仍满足B树的定义.

NO.3:如果被删除的结点的关键字数等于(m-1)/2,并且该结点的左右兄弟(如果存在的话)的关键字数目
也都为(m-1)/2,这时,需要删除关键字的结点与其左(或右)兄弟结点以及双亲结点中分割二者的
关键字合并成一个新的结点。如果因此双亲结点中的关键字数小于(m-1)/2,则对双亲节点做同样
的处理,以至于可能对根结点做同样的处理以至于整棵树减少一层。
*/


文件"btree.h"

#include <iostream>
#include <queue>
using namespace std;

const int MAX_M=10; //B树的最大阶
typedef int KeyType; //关键字类型

class BTree;

//B树结点类
class BTNode
{
	friend BTree;
private:
	int keynum; //结点当前拥有的关键字数目
	KeyType key[MAX_M]; //m阶B树每个结点最多有m-1个关键字,
					   //所以key[0]不放数据,从key[1]开始放
	BTNode *parent; //指向父节点
	BTNode *ptr[MAX_M]; //孩子结点指针数组,m阶B树最多可以有m个孩子结点
public:
	int Get_Num()
	{
		return keynum;
	}
	void Print_Keys()
	{
		for(int i=1;i<=keynum;i++)
			cout<<key[i]<<"  ";
		cout<<endl;
	}
};

//用于搜索时返回结果用
struct Result
{
	BTNode *pt; //指向找到的结点
	int i;  //找到的关键字在结点中的序号1,...,m
	int tag;  //结果标志,1:找到 0:未找到
};

class BTree
{
private:
	int m; //B树的阶数
	int Max; //B树结点最多关键字数目 Max=m-1
	int Min; //B树结点最少关键字数目 Min=(m-1)/2
public:
	BTree():m(0),Max(0),Min(0)
	{
	}

	//初始化B树的三个参数
	void Init(int newm)
	{
		m=newm;
		Max=newm-1;
		Min=Max/2;
	}

	//B树查找算法
	Result Search_BTree(BTNode *root,KeyType k)
	{
		BTNode *p=root,*q=NULL;	//q指向p的父节点
		int found=0,i=0;
		Result r;

		while(p && found == 0 )
		{
			i=Search(p,k);
			//在p->key[1,..,keynum]中查找k,使得p->key[i] <= k < p->key[i+1]
			if(i>0 && p->key[i] == k)
				found=1;
			else
			{
				q=p;
				p=p->ptr[i];
			}
		}
		r.i=i;
		if(found == 1)
		{
			r.pt=p;
			r.tag=1;
		}
		else
		{
			//查找不成功,q指向可插入k的结点
			r.pt=q; 
			r.tag=0;
		}
		return r;
	}

	//辅组查找函数
	int Search(BTNode *p,KeyType k)
	{
		//在p->key[1,..,keynum]中查找k,使得p->key[i] <= k < p->key[i+1]
		for(int i=0 ; i<p->keynum && p->key[i+1] <= k ; i++);
		return i;
	}

	//插入函数
	void Insert_BTree(BTNode *&root,KeyType k,BTNode *q,int i)
	{
		//在树的*q结点的key[i]和key[i+1]之间插入关键字k
		//如果插入过程中,结点关键字数目超出范围,则沿着
		//双亲链进行必要的结点分裂调整,使树root仍为m阶树
		//q与i都是由搜索函数所返回的Result结构体中的信息

		BTNode *ap;
		int finished,NeedNewRoot,s;
		KeyType x;
		if(q == NULL)
			NewRoot(root,NULL,k,NULL); //树为空,生成仅含关键字k的根节点root
		else
		{
			x=k;
			ap=NULL;
			finished=NeedNewRoot=0;
			while(NeedNewRoot == 0 && finished == 0)
			{
				Insert(q,i,x,ap);
				//将x和ap插入到q->key[i+1]和q->ptr[i+1]

				if(q->keynum <= Max)
					finished=1; //插入完成
				else
				{
					//分裂结点*q,将q->key[s+1,..,m],q->ptr[s,...m]
					//移到新结点*ap
					s=(m+1)/2;
					Split(q,ap);
					x=q->key[s];
					if(q->parent)
					{
						//在双亲结点中查找x的插入位置
						q=q->parent;
						i=Search(q,x);
					}
					else
						NeedNewRoot=1;
				}
			}
			if(NeedNewRoot == 1)   //根节点已经分裂成*q和*ap,生成新的根节点root,q和ap为子树指针
				NewRoot(root,q,x,ap);
		}
	}

	//辅组插入函数
	void Insert(BTNode *&q,int i,KeyType x,BTNode *ap)
	{
		//将x和ap插入到q->key[i+1]和q->ptr[i+1]
		int j;
		for(j=q->keynum; j>i ; j--)
		{
			//空出一个位置来放x与ap
			q->key[j+1]=q->key[j];
			q->ptr[j+1]=q->ptr[j];
		}
		q->key[i+1]=x;
		q->ptr[i+1]=ap;
		if(ap)
			ap->parent=q;
		q->keynum++;
	}

	//分裂函数
	void Split(BTNode *&q,BTNode *&ap)
	{
		//将结点*q分裂成两个部分,前一半保留,后一半移入新结点*ap
		int i,s=(m+1)/2;
		ap=new BTNode;
		ap->ptr[0]=q->ptr[s];
		for(i=s+1;i<=m;i++)
		{
			ap->key[i-s]=q->key[i];
			ap->ptr[i-s]=q->ptr[i];
		}
		ap->keynum=q->keynum-s;
		ap->parent=q->parent;
		for(i=0;i<=q->keynum-s;i++)
			if(ap->ptr[i])
				ap->ptr[i]->parent=ap;
		q->keynum=s-1;
	}

	//生成新的根节点
	void NewRoot(BTNode *&root,BTNode *p,KeyType x,BTNode *ap)
	{
		root=new BTNode;
		root->keynum=1;
		root->ptr[0]=p;
		root->ptr[1]=ap;
		root->key[1]=x;
		if(p)
			p->parent=root;
		if(ap)
			ap->parent=root;
		root->parent=NULL;
	}
	
	//通过组个插入的方法来创建一棵B树
	BTNode *Create_BTree(KeyType a[],int n)
	{
		Result r;
		BTNode *root=NULL;
		int i;
		for(i=0;i<n;i++)
		{
			r=Search_BTree(root,a[i]);
			if(r.tag == 0)
				Insert_BTree(root,a[i],r.pt,r.i);
		}
		return root;
	}

	//按照深度优先的层次顺序输出每个结点的关键字值
	void Print(BTNode *root,int layer,int num)
	{
		int i;
		BTNode *p=root;
		if(root == NULL)
			return;
		else
		{
			cout<<"第 "<<layer<<" 层第 "<<num<<" 个结点:";
			for(i=1;i<=p->keynum;i++)
				cout<<p->key[i]<<"  ";
			cout<<endl;
			cout<<endl;
			num=1;
			for(i=0;i<=p->keynum;i++)
			{
				if(p->ptr[i])
					Print(p->ptr[i],layer+1,num++);
			}
		}
	}


	//在树中删除关键字k
	int Delete_BTree(BTNode *&root,KeyType k)
	{
		BTNode *p; //用于释放一个空的root
		if(RecDelete(root,k) == 0)
		{
			cout<<"关键字 "<<k<<" 不存在B树中"<<endl;
			return 0;
		}
		else if(root->keynum == 0)
		{
			//根节点为空
			p=root;
			root=root->ptr[0];
			delete p;
		}
		return 1;
	}

	int RecDelete(BTNode *p,KeyType k)
	{
		//查找并删除关键字
		int i,found;
		if(!p)
			return 0;
		else
		{
			if( (found=SearchNode(p,k,i)) == 1)
			{
				if(p->ptr[i-1]) //非叶子结点
				{
					Successor(p,i); //找后代中最小的那个来代替它
					RecDelete(p->ptr[i],p->key[i]);
				}
				else
					Remove(p,i); //在结点*p的位置i删除关键字
			}
			else
				found=RecDelete(p->ptr[i],k);
			if(p->ptr[i])
				if(p->ptr[i]->keynum < Min)
					Restore(p,i);   //删除后关键字数目少于MIN,需要进行合并处理
			return found;
		}
	}

	int SearchNode(BTNode *p,KeyType k,int &i)
	{
		//搜索关键字在结点p中的位置
		if(k<p->key[1])
		{
			i=0;
			return 0;
		}
		else
		{
			i=p->keynum;
			while(k < p->key[i] && i>1)
				i--;
			return(k == p->key[i]);
		}
	}

	void Remove(BTNode *p,int i)
	{
		//在结点*p的位置i删除关键字
		int j;
		for(j=i+1;j<=p->keynum;j++)
		{
			p->key[j-1]=p->key[j];
			p->ptr[j-1]=p->ptr[j];
		}
		p->keynum--;
	}

	void Successor(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 Restore(BTNode *p,int 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);
	}

	void MoveRight(BTNode *p,int i)
	{
		//将p->ptr[i-1]中的一个关键字移动到双亲中,双亲一个关键字移动到p->ptr[i]中
		int c;
		BTNode *t=p->ptr[i];
		for(c=t->keynum;c>0;c--)
		{
			t->key[c+1]=t->key[c];
			t->ptr[c+1]=t->ptr[c];
		}
		t->ptr[1]=t->ptr[0];
		t->keynum++;
		t->key[1]=p->key[i];
		
		t=p->ptr[i-1];

		p->key[i]=t->key[t->keynum];
		p->ptr[i]->ptr[0]=t->ptr[t->keynum];
		t->keynum--;
	}

	void MoveLeft(BTNode *p,int i)
	{
		//将p->ptr[i]中的一个关键字移动到双亲中,双亲一个关键字移动到p->ptr[i-1]中
		int c;
		BTNode *t;
		t=p->ptr[i-1];
		t->keynum++;
		t->key[t->keynum]=p->key[i];
		t->ptr[t->keynum]=p->ptr[i]->ptr[0];
		t=p->ptr[i];
		p->key[i]=t->key[1];
		t->ptr[0]=t->ptr[1];
		t->keynum--;
		for(c=1;c<=t->keynum;c++)
		{
			t->key[c]=t->key[c+1];
			t->ptr[c]=t->ptr[c+1];
		}
	}

	void Combine(BTNode *p,int i)
	{
		//将结点*p的左右孩子和父节点的第一个关键字结合,然后去掉右孩子
		int c;
		BTNode *q=p->ptr[i];
		BTNode *l=p->ptr[i-1];
		l->keynum++;
		l->key[l->keynum]=p->key[i];
		l->ptr[l->keynum]=q->ptr[0];
		for(c=1;c<=q->keynum;c++)
		{
			l->keynum++;
			l->key[l->keynum]=q->key[c];
			l->ptr[l->keynum]=q->ptr[c];
		}
		for(c=i;c<p->keynum;c++)
		{
			p->key[c]=p->key[c+1];
			p->ptr[c]=p->ptr[c+1];
		}
		p->keynum--;
		delete q;
	}
};

测试文件

#include "btree.h"
#include <iostream>
using namespace std;

int main()
{
	BTree tree;
	BTNode *root=NULL;
	Result r;
	int m,n,pos;
	KeyType temp;
	//KeyType *a;
	KeyType a[20]={1,2,6,7,11,4,8,13,10,5,17,9,16,20,3,12,14,18,19,15};

	cout<<"输入你要建立的B-树的阶数(3-10): ";
	cin>>m;
	while(m<3 ||m>10)
	{
		cout<<"输入的阶数不满足要求(3-10),请重新输入:";
		cin>>m;
	}
	tree.Init(m);
	/*
	cout<<"输入要初始化B树的数组的大小:";
	cin>>n;
	a=new KeyType[n];
	cout<<"输入 "<<n<<" 个关键字数据:"<<endl;
	for(int i=0;i<n;i++)
		cin>>a[i];
	*/
	n=20;
	root=tree.Create_BTree(a,n);
	cout<<endl;
	cout<<"B树的每个结点的结构如下,每个结点一行,按照从上到下从左到右的顺序"<<endl;
	cout<<endl;
	tree.Print(root,1,1);
	
	cout<<"输入你要查找的关键字(输入-1退出):"<<endl;
	while(cin>>temp)
	{
		if(temp == -1)
			break;
		r=tree.Search_BTree(root,temp);
		if(r.tag == 1)
		{
			cout<<endl;
			cout<<"关键字在B树中,所在结点的关键字总数是:"<<r.pt->Get_Num()<<endl;
			cout<<"该结点所有关键字为:";
			r.pt->Print_Keys();
			cout<<endl;
		}
		else
			cout<<"该关键字不在B-树中"<<endl;
	}

	cout<<"输入你要删除的关键字(输入-1退出):"<<endl;
	while(cin>>temp)
	{
		if(temp == -1)
			break;
		else
		{
			temp=tree.Delete_BTree(root,temp);
			if(temp == 1)
			{
				cout<<"删除结点后的树的结构如下:"<<endl;
				tree.Print(root,1,1);
			}
			cout<<endl;
		}
	}
	return 0;
}


运行结果:

输入你要建立的B-树的阶数(3-10): 5

B树的每个结点的结构如下,每个结点一行,按照从上到下从左到右的顺序

第 1 层第 1 个结点:10

第 2 层第 1 个结点:3  6

第 3 层第 1 个结点:1  2

第 3 层第 2 个结点:4  5

第 3 层第 3 个结点:7  8  9

第 2 层第 2 个结点:13  16

第 3 层第 1 个结点:11  12

第 3 层第 2 个结点:14  15

第 3 层第 3 个结点:17  18  19  20

输入你要查找的关键字(输入-1退出):
8

关键字在B树中,所在结点的关键字总数是:3
该结点所有关键字为:7  8  9

12

关键字在B树中,所在结点的关键字总数是:2
该结点所有关键字为:11  12

-1
输入你要删除的关键字(输入-1退出):
12
删除结点后的树的结构如下:
第 1 层第 1 个结点:3  6  10  16

第 2 层第 1 个结点:1  2

第 2 层第 2 个结点:4  5

第 2 层第 3 个结点:7  8  9

第 2 层第 4 个结点:11  13  14  15

第 2 层第 5 个结点:17  18  19  20


8
删除结点后的树的结构如下:
第 1 层第 1 个结点:3  6  10  16

第 2 层第 1 个结点:1  2

第 2 层第 2 个结点:4  5

第 2 层第 3 个结点:7  9

第 2 层第 4 个结点:11  13  14  15

第 2 层第 5 个结点:17  18  19  20


9
删除结点后的树的结构如下:
第 1 层第 1 个结点:3  6  11  16

第 2 层第 1 个结点:1  2

第 2 层第 2 个结点:4  5

第 2 层第 3 个结点:7  10

第 2 层第 4 个结点:13  14  15

第 2 层第 5 个结点:17  18  19  20


-1
Press any key to continue

生成的5阶B树


  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 4
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值