B-树的实现(二)C语言实现

虽然前面把伪代码进行了阐述,但是想要真正的编写出来还是非常的不容易,鉴于能力有限,本人的实现版本如下(和伪代码差距有点大,码力差距还是和别人有很大)

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

#define m 4

//B_树的数据结构
typedef struct node{
	int Numbers;
	int val[m+1];
	char Key[m+1];
	struct node *ptr[m+1];
	struct node *Parent;
}BNode,*PBNode;

//传递查找信息
typedef struct re{
	bool tag;                    //1表示查找到,0表示未查找到
	int Index;                   //查找到的下标
	PBNode Result;               //记录最后查找的节点
}Record;

//插入Key和val,P是要插入的节点,Root是树的根节点,返回0表示是刷新原来的值
int InsertKey(PBNode *Root,PBNode P,char Key,int val);

//查找Key并且返回含有Key的节点的相关信息
bool SearchKey(PBNode Root,Record *Result,char Key);

//在指定节点查找,但是并不一定会查找到,有可能查找的是插入的下标
int Search(PBNode Node,char Key);

//执行在指定节点插入的函数
void InsertInNode(PBNode p,int InsertIndex,char Key,int val);

//分裂函数,当插入时候子节点的个数大于M的时候就要进行分裂,将前一半的数据留在原来的节点,后一半的数据放入新节点
PBNode Split(PBNode Node);

//利用插入函数的多次调用直接创建一棵B-树
void CreateTree(PBNode *Root,char *Key,int *Data,int KeySize);

//采用类似先序遍历的方法来进行B-树的遍历
void Display(PBNode Root);

//从左兄弟借键
void LeftSilb(PBNode Left,PBNode right,PBNode Parent,int DeleteIndex);

//从右兄弟借键
void RightSilb(PBNode Left,PBNode right,PBNode Parent,int DeleteIndex);

//当左右节点都没有键可以借用的时候,从父节点借用并且合并,和左节点合并
void MergeWithLeft(PBNode Left,PBNode Parent,PBNode Right,int DeleteIndex,bool flag);

//当左右节点都没有键可以借用的时候,从父节点借用并且合并,和右节点合并
void MergeWithRight(PBNode Left,PBNode Parent,PBNode Right,int DeleteIndex,bool flag);

//删除指定的键值
bool DeleteKey(PBNode *Root,char Key);

//判断节点是否是叶子节点true代表是叶子节点
bool IsLeave(PBNode Node);

//在删除的时候需要迭代的情况
void IterativeDelete(PBNode *Root,PBNode p,int Border,int Index);

//Modify Parent Point
void ModifyParent(PBNode Root);

//如果删除的是非叶子节点则需要转换,选择左节点的最大值,Index是要交换的Key位置
PBNode LeftMaxToNode(PBNode p,int Index);

//如果删除的是非叶子节点则需要转换,选择右节点的最小值,Index是要交换的Key的位置
PBNode RightMinToNode(PBNode p,int Index);

由于代码的冗余,可能代码量有点多,所以我只贴出最关键的函数实现:

//Modify Parent Point
void ModifyParent(PBNode Root)
{
	int i;
	if(!Root)
		return;
	for(i = 0;i < Root->Numbers;i++)
	{
		if(Root->ptr[i])
			Root->ptr[i]->Parent = Root;
		ModifyParent(Root->ptr[i]);
	}
	//最右边的分支
	ModifyParent(Root->ptr[i]);
	if(Root->ptr[i])
		Root->ptr[i]->Parent = Root;
}


//在指定节点查找,但是并不一定会查找到,有可能查找的是插入的下标
int Search(PBNode Node,char Key)
{
	if(!Node)
		return -1;
	int i,j = -1;
	for(i = 0;i < Node->Numbers;i++)
	{
		if(Node->Key[i] < Key)
			j = i;
	}
	return j;
}

//查找Key并且返回含有Key的节点的相关信息
bool SearchKey(PBNode Root,Record *Result,char Key)
{
	int Index = Search(Root,Key);
	bool flag = false;
	PBNode Temp = Root;
	PBNode Parent = Root;
	int LastIndex = Index+1;
	while(!flag)           
	{
		//表示没查找到,但是返回应该要插入的叶子节点的信息
		if(!Temp)
		{
			(*Result).Index = LastIndex;
			(*Result).Result = Parent;
			(*Result).tag = 0;
			return false;
		}
		if(Temp->Key[Index+1] == Key)
		{
			(*Result).Index = Index+1;           
			(*Result).Result = Temp;
			(*Result).tag = 1;                   //表示查找到
			flag = true;
			return flag;
		}//if
		Parent = Temp;
		Temp = Temp->ptr[Index+1];
		LastIndex = Index+1;
		Index = Search(Temp,Key);  //因为Root->Key[Index]是小于Key的并且Root->Key[Index+1]是大于Key的
	}//while
	return false;
}
//分裂函数,当插入时候子节点的个数大于M的时候就要进行分裂,将前一半的数据留在原来的节点,后一半的数据放入新节点
PBNode Split(PBNode Node)
{
	//m/2始终是分裂提上父节点的Key
	int mid = m/2,i,j;   
	PBNode RightNode = (PBNode)malloc(sizeof(BNode));
	for(i = mid+1;i < Node->Numbers;i++)
	{
		RightNode->Key[i-mid-1] = Node->Key[i];
		RightNode->val[i-mid-1] = Node->val[i];
		RightNode->ptr[i-mid-1] = Node->ptr[i];
		if(RightNode->ptr[i-mid-1])
			RightNode->ptr[i-mid-1]->Parent = RightNode;
	}
	//最后一个分支
	RightNode->ptr[i-mid-1] = Node->ptr[i];
	if(RightNode->ptr[i-mid-1])
		RightNode->ptr[i-mid-1]->Parent = RightNode;
	for(j = i-mid;j < m+1;j++)
		RightNode->ptr[j] = NULL;
	//将原来节点的右侧分支全部断开
	for(j = m/2+1;j < Node->Numbers+1;j++)
		Node->ptr[j] = NULL;
    //便于调试
	for(i = mid+1;i < Node->Numbers;i++)
		Node->Key[i] = ' ';
	RightNode->Numbers = Node->Numbers-mid-1;
	Node->Numbers = mid;
	return RightNode;
}

//插入Key和val,P是要插入的节点,Root是树的根节点,返回0表示是刷新原来的值
int InsertKey(PBNode *Root,char Key,int val)
{
	Record Result;
    PBNode p;
	int Index,k;
	if(SearchKey((*Root),&Result,Key))
	{
		Result.Result->val[Result.Index] = val;
		return 0;											//表示只是刷新值
	}
	p = Result.Result;
	Index = Result.Index;
	bool finish = false;
	if(p)
	{
		InsertInNode(p,Index,Key,val);
		if(p->Numbers < m)
		  return true;
	}
	//此处的循环是为了出现多次分裂的情况!
	while(!finish && p)
	{
		//出现分裂的情况
		//else
		//{
			PBNode Right = Split(p);
			if(p->Parent)
			{
				//寻找要插入到父节点的哪个位置
				Index = Search(p->Parent,p->Key[m/2])+1;
				//将Key插入父亲节点
				InsertInNode(p->Parent,Index,p->Key[m/2],p->val[m/2]);
				if(p->Parent->Numbers < m)
					finish = true;
				//便于调试
				//p->Key[m/2] = ' '; 
				p->Parent->ptr[Index] = p;
				p->Parent->ptr[Index+1] = Right;
				Right->Parent = p->Parent;
				Key = p->Key[m/2];
				val = p->val[m/2];
				//便于调试
				p->Key[m/2] = ' '; 
				p = p->Parent;
			}
			//表示需要分裂父节点
			else
			{
				PBNode NewParent = (PBNode)malloc(sizeof(BNode));
				NewParent->Numbers = 1;
				NewParent->Key[0] = p->Key[m/2];
				NewParent->val[0] = p->val[m/2];
				NewParent->Parent = NULL;
				NewParent->ptr[0] = p;
				NewParent->ptr[1] = Right;
				p->Parent = Right->Parent = NewParent;
				//便于调试
				p->Key[m/2] = ' '; 
				(*Root) = NewParent;
				for(k = 2;k < m+1;k++)
					NewParent->ptr[k] = NULL;
				finish = true;
			}
		//}
	}//while
	//表示空节点
	if(!p)
	{
		(*Root) = (PBNode)malloc(sizeof(BNode));
		(*Root)->Key[0] = Key;
		(*Root)->val[0] = val;
		for(k = 0;k < m+1;k++)
			(*Root)->ptr[k] = NULL;
		(*Root)->Numbers = 1;
		(*Root)->Parent = NULL;
	}
	return 1;                                                //一定会成功
}
//当左右节点都没有键可以借用的时候,从父节点借用并且合并,和左节点合并,右子树有Key需要删除
void MergeWithLeft(PBNode Left,PBNode Parent,PBNode Right,int DeleteIndex,bool flag)
{
	int i,Way;
	//判断分支
	for(i = 0;i < Parent->Numbers;i++)
	{
		if(Left == Parent->ptr[i])
			break;
	}
	Way = i;
	//右子树的装填,删除并且从父亲那里借键
	for(i = DeleteIndex-1;i >= 0;i--)
	{
		Right->Key[i+1] = Right->Key[i];
		Right->ptr[i+1] = Right->ptr[i];
		Right->val[i+1] = Right->val[i];
	}
	Right->Key[0] = Parent->Key[Way];
	Right->val[0] = Parent->val[Way];
	//Right->ptr[0] = NULL;
	if(flag)
		Right->Numbers++;
	//合并,直接采用原来左边的节点
	for(i = 0;i < Right->Numbers;i++)
	{
		Left->Key[Left->Numbers+i] = Right->Key[i];
		Left->val[Left->Numbers+i] = Right->val[i];
		if(i != 0)
		{
			Left->ptr[Left->Numbers+i] = Right->ptr[i];
			if(Right->ptr[i])
				Right->ptr[i]->Parent = Left->ptr[Left->Numbers+i];
		}
	}
	Left->ptr[Left->Numbers+i] = Right->ptr[i-1];
	if(Right->ptr[i-1])
		Right->ptr[i-1] = Left->ptr[Left->Numbers+i];
	Left->Numbers = Left->Numbers+Right->Numbers;
	//父节点后面的节点向前进行一个搬迁
	//for(i = Way+1;i < Parent->Numbers-1;i++)
	//{
	//	Parent->Key[i] = Parent->Key[i+1];
	//	Parent->val[i] = Parent->val[i+1];
	//	Parent->ptr[i] = Parent->ptr[i+1];
	//}
	//Parent->ptr[Parent->Numbers-1] = Parent->ptr[Parent->Numbers];
	Parent->Numbers--;
}
//如果删除的是非叶子节点则需要转换,选择左节点的最大值
PBNode LeftMaxToNode(PBNode p,int Index)
{
	PBNode Temp;
	int i,Remember = Index;
	for(i = 0;i <= Index;i++)
	{
		if(p->ptr[i])
			break;
	}
	//左边节点全为空
	if(i == Index+1)
		return NULL;
	Temp = p->ptr[i];
    //左节点的最大值(右边)
	while(!IsLeave(Temp))
	{
		for(i = Temp->Numbers;i >= 0;i--)
		{
			if(Temp->ptr[i])
				break;
		}
		if(i == -1)
			break;
		Temp = Temp->ptr[i];
	}
	char T = p->Key[Remember];
	int TT = p->val[Remember];
	p->Key[Remember] = Temp->Key[Temp->Numbers-1];
	p->val[Remember] = Temp->val[Temp->Numbers-1];
	Temp->Key[Temp->Numbers-1] = T;
	Temp->val[Temp->Numbers-1] = TT;
	return Temp;
}
//删除指定的键值
bool DeleteKey(PBNode *Root,char Key)
{
	Record Re;
	int i,way;
	//并没有查找到,删除失败
	if(!SearchKey((*Root),&Re,Key))
		return false;
	bool finish = false;
	//Border是[m/2]-1向上取整
	int Border = (m%2==0) ? (m/2-1):(m/2); 
	PBNode p = Re.Result;
	//for(i = 0;i < p->Parent->Numbers;i++)
	//{
	//	if(p->Parent->ptr[i] == p)
	//		break;
	//}
	//要删除的节点是父节点的第几个分支
	//Way = i;  
	//用于迭代的进入条件
	if(!(p->Parent) && IsLeave(p))
	{
		for(i = Re.Index;i < p->Numbers;i++)
		{
			p->Key[i] = p->Key[i+1];
			p->val[i] = p->val[i+1];
		}
		p->Numbers--;
		return true;
	}
	bool flag = false;
	while(!finish)
	{
		if(!(p->Parent) && IsLeave(p))
		{
			for(i = Re.Index;i < p->Numbers;i++)
			{
				p->Key[i] = p->Key[i+1];
				p->val[i] = p->val[i+1];
			}
			p->Numbers--;
			return true;
		}
		if(p->Parent)
		{
			for(i = 0;i < p->Parent->Numbers+1;i++)
			{
				if(p->Parent->ptr[i] == p)
					break;
			}		
			//要删除的节点是父节点的第几个分支
			way = i;  
		}
		else
			way = 0;
		//叶子节点并且节点个数大于Border
		if(IsLeave(p) && p->Numbers > Border)
		{
			for(i = Re.Index;i < p->Numbers;i++)
			{
				p->Key[i] = p->Key[i+1];
				p->val[i] = p->val[i+1];
			}
			p->Numbers--;
			finish = true;
		}
		//叶子节点且是第一个分支
		else if((IsLeave(p) || flag) && way == 0)
		{
			//右兄弟可以借键
			if(p->Parent->ptr[1]->Numbers > Border)
			{
				RightSilb(p,p->Parent->ptr[1],p->Parent,Re.Index);
				finish = true;
			}
			//右兄弟没有多余的键
			else
			{
				MergeWithRight(p,p->Parent,p->Parent->ptr[1],Re.Index,flag);	
				//是否需要继续递归
				if(p->Parent->Numbers >= Border || (!(p->Parent->Parent)&&p->Parent->Numbers > 0))
				{
					//因为结束了,所以要modify父节点
					for(i = way;i < p->Parent->Numbers;i++)
					{
						p->Parent->Key[i] = p->Parent->Key[i+1];
						p->Parent->val[i] = p->Parent->val[i+1];
					}
					for(i = 1;i < p->Parent->Numbers+1;i++)
						p->Parent->ptr[i] = p->Parent->ptr[i+1];
					//便于调试
					p->Parent->Key[p->Parent->Numbers] = ' ';
					p->Parent->val[p->Parent->Numbers] = -1;
					p->Parent->ptr[p->Parent->Numbers+1] = NULL;
					finish = true;
				}
				//表示根节点被分解,那么现在的节点成为根节点
				else if(p->Parent->Numbers == 0 && !(p->Parent->Parent))
				{
					(*Root) = p;
					(*Root)->Parent = NULL;
					finish = true;
				}
				else
				{
					p = p->Parent;
					Re.Index = 0;
					flag = true;
				}
			}
		}
		//叶子节点且是最后一个分支
		else if((IsLeave(p) || flag) && way == p->Parent->Numbers)
		{
			//左兄弟可以借键
			if(p->Parent->ptr[p->Parent->Numbers-1]->Numbers > Border)
			{
				LeftSilb(p->Parent->ptr[p->Parent->Numbers-1],p,p->Parent,Re.Index);
				finish = true;
			}
			//左兄弟没有多余的键
			else
			{
				MergeWithLeft(p->Parent->ptr[p->Parent->Numbers-1],p->Parent,p,Re.Index,flag);
				//是否需要继续递归
				if(p->Parent->Numbers >= Border || (!(p->Parent->Parent)&&p->Parent->Numbers > 0))
				{
					//因为结束了,所以要modify父节点
					for(i = way-1;i < p->Parent->Numbers;i++)
					{
						p->Parent->Key[i] = p->Parent->Key[i+1];
						p->Parent->val[i] = p->Parent->val[i+1];
					}
					//for(i = way;i < p->Parent->Numbers+1;i++)
					//	p->Parent->ptr[i] = p->Parent->ptr[i+1];
					//便于调试
					p->Parent->Key[p->Parent->Numbers] = ' ';
					p->Parent->val[p->Parent->Numbers] = -1;
					p->Parent->ptr[p->Parent->Numbers+1] = NULL;
					finish = true;
				}
				//表示根节点被分解,那么现在的节点成为根节点
				else if(p->Parent->Numbers == 0 && !(p->Parent->Parent))
				{
					(*Root) = p->Parent->ptr[way-1];
					(*Root)->Parent = NULL;
					finish = true;
				}
				else
				{
					p = p->Parent;
					Re.Index = p->Numbers-1;
					flag = true;
				}
			}
		}
		//叶子节点,但是是中间节点
		else if((IsLeave(p) || flag) && 0 < way && way < p->Parent->Numbers)
		{
			//左兄弟有多余的键
			if(p->Parent->ptr[way-1]->Numbers > Border)
			{
				LeftSilb(p->Parent->ptr[way-1],p,p->Parent,Re.Index);
				finish = true;
			}
			//右兄弟有多余的键
			else if(p->Parent->ptr[way+1]->Numbers > Border)
			{
				RightSilb(p,p->Parent->ptr[way+1],p->Parent,Re.Index);
				finish = true;
			}
			//左右兄弟都没有多余的键,选取左子树进行合并
			else
			{
				MergeWithLeft(p->Parent->ptr[way-1],p->Parent,p,Re.Index,flag);
				//是否需要继续递归
				if(p->Parent->Numbers >= Border || (!(p->Parent->Parent)&&p->Parent->Numbers > 0))
				{
					//因为结束了,所以要modify父节点
					for(i = way-1;i < p->Parent->Numbers;i++)
					{
						p->Parent->Key[i] = p->Parent->Key[i+1];
						p->Parent->val[i] = p->Parent->val[i+1];
					}
					//便于调试
					p->Parent->Key[p->Parent->Numbers] = ' ';
					p->Parent->val[p->Parent->Numbers] = -1;
					for(i = Re.Index+1;i < p->Parent->Numbers+1;i++)
						p->Parent->ptr[i] = p->Parent->ptr[i+1];
					p->Parent->ptr[p->Parent->Numbers+1] = NULL;
					finish = true;
				}
				//表示根节点被分解,那么现在的节点成为根节点
				else if(p->Parent->Numbers == 0 && !(p->Parent->Parent))
				{
					(*Root) = p->Parent->ptr[way-1];
					(*Root)->Parent = NULL;
					finish = true;
				}
				else
				{
					p = p->Parent;
					Re.Index = 0;
					flag = true;
				}
			}
		}
		//非叶子节点
		else
		{
			p = LeftMaxToNode(p,Re.Index);
			if(p)
				Re.Index = p->Numbers-1;
			else
			{
				p = RightMinToNode(p,Re.Index);
				Re.Index = 0;
			}
		}
	}//while
	ModifyParent(*Root);
}

以上!

更加简便的见这位仁兄的:https://blog.csdn.net/geek_jerome/article/details/78895289

猜你喜欢

转载自blog.csdn.net/c_living/article/details/81104896