B-树插入、删除

关于B-树基础知识可以参看:

http://student.zjzk.cn/course_ware/data_structure/web/chazhao/chazhao9.3.2.1.htm

B-树的数据结构如下:

class TreeNode
{
	private:
		int keynum;
		TreeNode* parent;
		TreeNode* ptr[m+1];//0...m use to store pointer
		int key[m+1];//1...m use to store k
	public:
		TreeNode()
		{
			keynum = 0;
		}
		TreeNode(TreeNode* p1, int k, TreeNode *p2)//root node
		{
			keynum = 1;
			key[1] = k;
			parent = 0;
			fill(ptr, ptr+m+1, (TreeNode*)0);
			ptr[0] = p1;
			ptr[1] = p2;

			if(p1)
				p1->parent = this;
			if(p2)
				p2->parent = this;
		}
		TreeNode(TreeNode* p, int *keyarry, TreeNode **ptrarry)
		{
			parent = p;
			fill(ptr, ptr+m+1, (TreeNode*)0);
			int temp = ceil(m/2.0);//rounding up
			ptr[0] = ptrarry[temp];
			if(ptr[0])
				ptr[0]->parent = this;
			for(int i = 1; i+temp <= m; ++i)
			{
				key[i] = keyarry[i+temp];
				ptr[i] = ptrarry[i+temp];
				if(ptr[i])
					ptr[i]->parent = this;
				keyarry[i+temp] = 0;
				ptrarry[i+temp] = 0;
			}
			keyarry[temp] = 0;
			ptrarry[temp] = 0;
			keynum = m-temp;
		}
		friend class Result;
		friend class Btree;
};
class Result
{
	public:
		TreeNode* Resultptr;
		int index;
		bool Resultflag;
		Result(TreeNode* p = 0, int i = 0, bool r=0)
			:Resultptr(p), index(i), Resultflag(r)
		{}
		friend class Btree;
};
class Btree
{
	public:
		Btree()
		{
			root = 0;
		}
		void InsertBtree(const int k);
		Result Find(const int k)const;
		void DeleteBtree(const int k);
		void Insert(const int k, TreeNode* node, TreeNode* a);
		void Display()const;
		void Search(const int k)const;
	private:
		TreeNode* root;
		/*for delete*/
		void BorrowOrCombine(TreeNode* a, const int i, const int type, stack<int>& s);
};

          其中TreeNode是B-树的结点,TreeNode在private里包含了结点所有的信息,同时包含了三个构造函数。其中第一个不带参数的构造函数,只是为了初始化空节点;第二个构造函数为了创建B-树的根节点;第三个构造函数,包含三个参数,目的是为了在插入的时候分裂a结点,其中p是a结点的parent指针,keyarry、ptrarry分别为结点a的key数组以及结点a的ptr指针数组,a分裂后产生的新的结点的父节点为p。

         Result类主要用于封装查找关键字的返回结果,其中Resultptr查找结果的B-树结点,如果包含key则返回该结点,如果不包含key则返回待插入值key的叶子节点,index为key在该节点中的位置或key将要插入的结点的位置,Resultflag表示是否包含key值。

         Btree类主要用来封装root结点和与B-树相关的函数。

         B-树的成员函数实现结果如下:

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

void Btree::InsertBtree(const int k)
{
	if(!root)
	{
		root = new TreeNode(0,k,0);
		return;
	}
	Result res = Find(k);
	TreeNode* a = res.Resultptr;
	if(res.Resultflag == true)
		return;
	Insert(k, 0, a);
}
void Btree::Search(const int k)const
{
	Result res = Find(k);
	if(res.Resultflag == true)
		cout<<"find it"<<endl;
	else 
		cout<<"not find"<<endl;
}
void Btree::Insert(const int k, TreeNode* node, TreeNode* a)
{
	int i = 1;//index to insert
	while(i <= a->keynum)
	{
		if(k <= a->key[i])
		{
			break;
		}
		i++;
	}
	for(int j = a->keynum; j >= i; --j)
	{
		a->key[j+1] = a->key[j];
		a->ptr[j+1] = a->ptr[j];
	}
	a->key[i] = k;
	a->ptr[i] = node;
	if(node)
		node->parent = a;
	++a->keynum;
	if(a->keynum  <= m-1 )
	{
		return;
	}
	else
	{
		/*slipt*/
		int midkey = a->key[(int)ceil(m/2.0)];
		TreeNode* newnode = new TreeNode(a->parent, a->key, a->ptr);
		a->keynum = m - ceil(m/2.0);
		TreeNode* tempa = a;//restore a
		a = a->parent;
		if(!a)
		{
			TreeNode* newRoot = new TreeNode(tempa,midkey,newnode);
			tempa->parent = newRoot;
			newnode->parent = newRoot;
			root = newRoot;
			return;
		}
		else
		{
			Insert(midkey,newnode,a);
		}
	}
}
Result Btree::Find(const int k)const
{
	if(!root)
	{
		cout<<"the tree is empty"<<endl;
		return Result(0,0,0);
	}
	TreeNode* a = root;
	int i = 1;
	while(a)
	{
		i = 1;
		while(i <= a->keynum)
		{
			if(a->key[i] >= k)
			{
				break;
			}
			else
				++i;
		}
		if(k == a->key[i])
		{
			return Result(a, i, 1);
		}
		else
		{
			if(a->ptr[i-1])
			{
				a = a->ptr[i-1];
			}
			else
			{
				return Result(a, i, 0);
			}
		}
	}
}
void Btree::DeleteBtree(const int k)
{
	if(!root)
	{
		cout<<"The tree is null!"<<endl;
		return;
	}

	stack<int> s;//store the all index
	TreeNode* delnode = root;
	int i = 1;
	
	while(delnode)
	{
		i = 1;
		while(i <= delnode->keynum )
		{
			if(k <= delnode->key[i])
			{
				break;
			}
			else
			{
				i++;
			}
		}
		if(k == delnode->key[i])
		{
			cout<<"find it"<<endl;
			break;
		}
		else
		{
			if(delnode->ptr[i-1] == 0)
			{
				cout<<"no this key"<<endl;
				return;
			}
			else
			{
				delnode = delnode->ptr[i-1];
					s.push(i-1);
			}
		}
	}
	TreeNode* p = delnode; //store delnode
	if(delnode->ptr[i])
	{
		s.push(i);
		p = delnode->ptr[i];
		while(p->ptr[0])
		{
			p = p->ptr[0];
			if(!p->ptr[0])
				break;
			s.push(0);
		}
	}
	if(p != delnode)
	{
		delnode->key[i] = p->key[1];
		i = 1;
	}

	BorrowOrCombine(p, i, 0, s);
}
void Btree::BorrowOrCombine(TreeNode* a,const int i,const int type, stack<int>& s)
{
	if(a == root && root->keynum == 1)
	{
		TreeNode* oldroot = root;	
		if(type == -1)
		{
			if(root->ptr[i-1])
				root = root->ptr[i-1];
			else 
				root = 0;
		}
		else if(type == 1)
		{
			if(root->ptr[i])
			{
				root = root->ptr[i];
			}
			else
				root = 0;
		}
		else
		{
			root = 0;
		}
		if(root)
			root->parent = 0;
		delete oldroot;
		return;
	}
	int minnum = ceil(m/2.0)-1;
	TreeNode *la,*ra;
	int j;
	if(a->keynum > minnum|| a == root)
	{
		TreeNode* tempstr;
		if(type == -1)
		{
			tempstr = a->ptr[i-1];
		}
		else
		{
			tempstr = a->ptr[i];
		}
		for(j = i; j < a->keynum; ++j)
		{
			a->key[j] = a->key[j+1]; 
			a->ptr[j] = a->ptr[j+1];
		}
		a->ptr[i-1] = tempstr;
		a->key[j] = 0;
		a->ptr[j] = 0;
		--a->keynum;
	}
	else
	{
		int index = s.top();
		s.pop();
		if(index)//have left brother
		{
			la = a->parent->ptr[index - 1];//left brother
			if(la->keynum > minnum)//left brother has enough elements
			{
				TreeNode* tempstr;
				if(type == -1)
				{
					tempstr = a->ptr[i-1];
				}
				else
				{
					tempstr = a->ptr[i];
				}
				for(j = i; j > 1; --j)
				{
					a->key[j] = a->key[j-1];
					a->ptr[j] = a->ptr[j-1];	
				}
				a->ptr[i] = tempstr;

				a->key[1] = a->parent->key[index];
				a->ptr[0] = la->ptr[la->keynum];
				if(la->ptr[la->keynum])
					la->ptr[la->keynum]->parent = a;
				a->parent->key[index] = la->key[la->keynum];
				la->ptr[la->keynum] = 0;
				la->key[la->keynum] = 0;
				--la->keynum;
				return;
			}
		}
		else if(index < a->parent->keynum)//have right brother
		{
			ra = a->parent->ptr[index+1];
			if(ra->keynum > minnum)// have lots of elements
			{
				TreeNode* tempstr;
				if(type == -1)
				{
					tempstr = a->ptr[i-1];
				}
				else
				{
					tempstr = a->ptr[i];
				}
				for(j = i; j < a->keynum; j++)
				{
					a->key[j] = a->key[j+1];
					a->ptr[j] = a->ptr[j+1]; 
				}
				a->ptr[i-1] = tempstr;

				a->key[a->keynum] = a->parent->key[index+1];
				a->ptr[a->keynum] = ra->ptr[0];
				if(ra->ptr[0])
					ra->ptr[0]->parent = a;
				a->parent->key[index+1] = ra->key[1];
				ra->ptr[0] = ra->ptr[1];
				for(j = 1; j < ra->keynum; j++)
				{
					ra->key[j] = ra->key[j+1];
					ra->ptr[j] = ra->ptr[j+1];
				}
				ra->key[ra->keynum] = 0;
				ra->ptr[ra->keynum] = 0;
				--ra->keynum;
				return;
			}
		}
		if(index)//have left brother donnot hava enough elements
		{
			cout<<la->key[1];
			la = a->parent->ptr[index-1];
			cout<<a->parent->key[index]<<a->key[i]<<endl;
			if(la->keynum == minnum)//donnot have enough elements
			{
				/*combine left brother*/
				la->key[la->keynum+1] = a->parent->key[index];
				for(j =1; j <= i-1; ++j)
				{
					la->ptr[la->keynum+j] = a->ptr[j-1];
					la->key[la->keynum+j+1] = a->key[j];
					if(a->ptr[j-1])
						a->ptr[j-1]->parent = la;
				}
				if(type == -1)
				{
					la->ptr[la->keynum+i] = a->ptr[i-1];
					if(a->ptr[i-1])
						a->ptr[i-1]->parent = la;
				}
				else
				{
					la->ptr[la->keynum+i] = a->ptr[i];
					if(a->ptr[i])
					{
						a->ptr[i]->parent = la;
					}
				}
				for(j = i+1; j <= a->keynum; ++j)
				{
					la->key[la->keynum+j] = a->key[j];
					la->ptr[la->keynum+j] = a->ptr[j];
					if(a->ptr[j])
						a->ptr[j]->parent = la;
				}
				la->keynum = m-1;

				TreeNode* tempp = a->parent;
				tempp->ptr[index] = 0;
				delete a;
				BorrowOrCombine(tempp,index,-1,s);
				return;
			}
		}
		if(index < a->keynum)//have right brother
		{
			ra = a->parent->ptr[index+1];
			cout<<"have right brother"<<endl;
			if( ra->keynum == minnum)// right brother do not have enough elements
			{
				int step = m - 1 - ra->keynum;
				for(j = ra->keynum; j >0; --j)
				{
					ra->key[j + step] = ra->key[j];
					ra->ptr[j + step] = ra->ptr[j];
				}
				ra->key[step] = a->parent->key[index+1];
				ra->ptr[step] = ra->ptr[j];
				--step;
				for(j = a->keynum; j > i; --j,--step)
				{
					ra->ptr[step] = a->ptr[j];
					if(a->ptr[j])
					{	
						a->ptr[j]->parent = ra;
					}
					ra->key[step] = a->key[j];
				}
				if(type == -1)
				{
					ra->ptr[step] = a->ptr[j - 1];
					if(a->ptr[j - 1])
					{
						a->ptr[j - 1]->parent = ra;
					}
				}
				else
				{
					ra->ptr[step] = a->ptr[j];
					if(a->ptr[j])
					{
						a->ptr[j]->parent = ra;
					}
				}
				for(int x = i-1, j = i-1; x > 0; --x, --j)
				{
					ra->key[x] = a->key[j];
					ra->ptr[x-1] = a->ptr[j-1];
					if(ra->ptr[x-1])
						ra->ptr[x-1]->parent = ra;
				}
				ra->keynum = m-1;
				TreeNode* ap = a->parent;
				ap->ptr[index] = 0;
				delete a;
				BorrowOrCombine(ap, index+1, 1, s);
			}
		}
	}
}

void Btree::Display()const
{
	if(!root)
	{
		cout<<"Btree is empty!"<<endl;
	}
	else
	{
		TreeNode* p = root;
		queue<TreeNode*> qu;
		while(p)
		{
			qu.push(p->ptr[0]);
			for(int j = 1; j<= p->keynum; j++)
			{
				if(p->ptr[j])
					qu.push(p->ptr[j]);
				cout<<p->key[j]<<"-";
			}
			cout<<"his parent"<<endl;
			TreeNode* q = p->parent;
			if(q)	
			{
				for(int j = 1; j<= q->keynum; j++)
				{
				//qu.push(q->ptr[j]);
					cout<<q->key[j]<<"-";
				}
			}
			cout<<"   ";
			p = qu.front();
			qu.pop();
			cout<<endl;
		}
	}
}

        其中,插入和删除操作是整个实现代码的核心。

        插入操作的思想是,先插入后分裂。即不管待插入结点的元素数量,先将元素插入到指定位置,然后再判断元素数量是否超过B-树的最大值,如果超越,通过TreeNode的第三个构造函数对结点进行分裂,分裂后将结点的中心值midkey插入到父节点的相应位置,再次判断,直到满足条件结束。代码通过不断递归实现。

        删除操作是B-树的难点和核心。删除一个key值首先判断key值在不在叶子节点,如果不在通过下面的方法将问题转化为删除叶子节点的一个key值。

        非叶子节点key值删除转换方法,首先将key中序后继(前驱,本代码选择后续)的key值代替待删除的key,也就是说使用当前节点key值的右子树的最左节点的第一个key值替换待删除的key,这样将问题转化为删除叶子节点的key值。

        删除叶子节点key值分为四种情况:

       1、当前叶子节点的keynum>minnum时,只需将待删除key值后面的可以key值以及ptr值顺序向前覆盖,即可完成。

       2、当前叶子节点的keynum == minum,叶子节点的左兄弟keynum > minnum,此时将叶子节点key值前面的key序列和ptr序列顺序后移一位,腾出第一个key值的位置和ptr[0]的位置,然后将对应的父节点的值移入到key[1],左兄弟的最后一个节点对应的指针赋值给当前节点ptr[0],左兄弟最后一个key值赋值给对应的父节点的key。即可完成。

       3、当前叶子节点的keynum== minum,叶子节点的右兄弟keynum >minnum,参照情况二。

       4、当前叶子节点的keynum == minum,叶子节点左兄弟为keynum ==minnum,这时候就需要对结点进行合并,将左节点、父节点以及当前节点合并,合并的时候需要注意,只将父节点对应的值置为0,不改变父节点的结构,同时传递一个有效指针的位置参数。即当前节点与左兄弟合并时,传递-1,表示父节点值对应的左边子树指针为有效节点,同理与右兄弟合并时传递1。继续以与左兄弟合并为例,左兄弟的内容不变,在后面加入父节点对应的值以及当前节点剩余的值以及指针,指针的顺序不改变。合并完成后,对父节点进行调整,我们知道父节点已经少了一个值,同时接收了标记参数type,接下来从1开始再次进行判断,知道递归完成。

       5、当前叶子节点的keynum == minum,叶子节点右兄弟为keynum ==minnum,参照情况4将当前节点、父节点对应的key值以及右兄弟合并。同时一步步递归直到完成。

       算法的一个比较重要的设计想法是,在进行合并操作时只进行本层节点的合并,对于父节点留作下一此迭代来完成,同时将有效指针的参数type传递给上层节点。

    原文作者:B树
    原文地址: https://blog.csdn.net/cyqllfh/article/details/15335777
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞