B树删除之迷魂大法

目标在叶节点

这种情况共有4种,我们只需要考虑,删除节点后当前节点需要满足的。
b树的删除操作,都以这张图为例,这里为2-3阶b树:
在这里插入图片描述

  1. 目标删除后,该节点的键数量≥规定度数/2
直接删除即可,注意,需要让B树键值数量做自减。

如图:删除图中的叶节点节点中的3.
在这里插入图片描述

  1. 目标删除后,该节点的键数量<规定度数/2
    优先向兄弟节点(可以是左,也可以是右)借键。
	如果是左,那么就借他的最大键;
	如果是右,那么就借他的最小键;
	得到之后,要记得对兄弟节点的键数量做自减
接着,再得到在父节点中,分离这两个节点的键,然后把这个键值赋给目标键所在的节点,删除目标键。

如图:本次删除的目标键为2
在这里插入图片描述
》》向兄弟借键时,需要考虑:

  • 当前目标节点在父节点的孩子数组中的位置是啥?上图中2所在的5,9节点中孩子数组的0索引处,这个时候,兄弟节点应该向右(父节点孩子数组索引为1的位置)。向右兄弟借键,就要借他的最小的键,否则,就违背了b树的定义。还有一个,就是取父节点的键值索引问题,因为向右节点借,需要取的是分离两个节点的键值,也就是5,刚好在父节点键数组的索引0位置处。
  • 假设现在删除10,此时,可以发现,10所在的5,9节点中孩子数组的最后一个(也就是父节点孩子数组索引为3,【n+1】,n表示节点的关键字数量,+1表示拥有孩子的个数),这个时候,就需要向左节点借max键。其次,就是父节点键的位置问题,这里的10在父节点数组的最后一个,所以取得父节点分离键值就是当前节点索引-1处,即9

向兄弟节点借键,还有一种比较复杂的问题。记住,我会在最后一种情况进行阐述的。

  • 若上述条件都不成立,那届时就处于一种很困的地步了。那就是合并了。
    warnning:套路较多,请系好安全带,以免晕车哦
    为避免晕车,我在这里先上一幅图,提前警备一下前方的坎坷路程。
    举一个特别糟糕的例子,如图:
    在这里插入图片描述
    删除5如图,一张图画不下,则会分开表示,请注意图的连续性哦。
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
    总结

  • 合并操作在叶节点的时候,只管合并,然后注意一下参与键值合并的节点的键值数量变化

  • 若在内部,重中之重的问题,就是还要合并两个节点的孩子,这里需要强调的是要严格遵循b树的定义,且看清楚兄弟节点的位置,然后进行父子相关联的操作

接下来就是有关于遇到父节点满足条件时进行合并的相关操作,与前面雷同,当在内部要进行这些操作时,也要进行两节点的孩子合并操作。但与借兄弟节点键不同,这个操作比借兄弟的要简单一些。

我们来回想一下,当删除的目标键在叶节点时,需要参照以下遵循:

 1.当够就删;
 2.当不够,就回溯;
 3.若有兄弟可借酒,兄弟位置要准瞅,接着退出;
 4.若兄弟不可借酒,就向父借酒,然后与兄弟共享之,但牢记要喊上孩子们,然后退出;
 5.若实在都不行,则继续合并;

上述都是我个人自己编的,其实旨在于帮助理解。

目标不在叶节点

目标在内部节点,这个时候,就要找与它挨边儿的来逐渐下沉,交替。我还是画个图来感官的理解一下。
参照上面的那幅图 本次删除20
在这里插入图片描述

接下来的删除操作,就参照目标在叶节点来进行。

代码部分

具体代码:(主要用于处理合并的操作)

my_B_tree* addr[SONNODECOUNT] = { nullptr };//存储节点地址。
	size_t keyArray[SONNODECOUNT] = { 0 };//存储键值
	my_B_tree* mergeNode = nullptr;
	while (1)
	{

		if ((parentNode->degress <= (DEGRESS >> 1) ||
			parentNode->degress > (DEGRESS >> 1)) &&
			brother->degress <= (DEGRESS >> 1))//合并
		{
			//此逻辑只能处理父节点是否满足删除后的键值是否满足1/2,并且兄弟节点不够借的情况。
			mergeNode = new my_B_tree;
			/*620--639    处理merge节点*/
			NodesToOne(mergeNode, brother, findNode);//将兄弟和父里面的键值合并到merge
			if (isRight == true) {
				mergeNode->key[mergeNode->degress++] = parentNode->key[levelPos];
				keyMobile(parentNode, levelPos);//重新将par节点的键对进行归位
			}
			else {
				mergeNode->key[mergeNode->degress++] = parentNode->key[levelPos - 1];
				keyMobile(parentNode, levelPos - 1);
			}
			int index = _insert_shellMode_Sort(mergeNode, key);//排序mergeNode节点
			if (false == isDelete) {//delKey是否删除
				keyMobile(mergeNode, index);
				mergeNode->degress--;
				isDelete = true;
			}
			/*对父节点进行移动,关联父与子节点的关系*/
			if (isRight == true)
			{
				move_Bro_Par_addr(parentNode, levelPos);
				parentNode->sonPt[levelPos] = mergeNode;//关联merge与parent的关系,将合并节点放在父节点的左边			
			}
			else
			{
				move_Bro_Par_addr(parentNode, levelPos - 1);
				parentNode->sonPt[levelPos - 1] = mergeNode;
			}
			parentNode->degress--;
			//par指针回溯到原始B树的根节点,此时,merge节点中的parent指向为空
			if (parentNode == tempRootNode)
			{
				mergeNode->parent = nullptr;
				mergeAddr(mergeNode, findNode, brother, isRight);//将find孩子、par孩子合并至merge
				FREE_MEMORY(parentNode);
				tempRootNode = mergeNode;
				FREE_MEMORY(findNode);
				FREE_MEMORY(brother);
				break;
			}
			else {//parent没有回到之前的临时根(tempRootNode)
				mergeNode->parent = parentNode;
			}
			mergeAddr(mergeNode, findNode, brother);//将 findNode, brother两个孩子的节点合并到mergeNode
			FREE_MEMORY(findNode);//释放findNode, brother
			FREE_MEMORY(brother);
			findNode = brother = nullptr;
			//if (flag)//parent节点删除后依然满足1/2,退出
			//	break;
			//find指针向上追溯
			findNode = mergeNode->parent;
			parentNode = parentNode->parent;
			levelPos = searchAdd(findNode, parentNode);
			if (levelPos == parentNode->degress) {
				isRight = false;//brother为左兄弟   控制与那个兄弟节点进行交换
				brother = parentNode->sonPt[levelPos - 1];
			}
			else
			{
				brother = parentNode->sonPt[levelPos + 1];
				isRight = true;
			}
		}
		else
		{
			//该逻辑只能处理单独向兄弟借的情况。
			//获取findNode,brother节点的孩子键值以及地址,存储在容器中,并将键值进行排序
			getSonNodeKey(findNode, brother, addr, keyArray, isRight);//error
			size_t parKey, broKey;
			modifyBroParent(parentNode, brother, &parKey, &broKey, levelPos, isRight);
			//将parKey, broKey分别放入到par节点和bro节点
			parentNode->key[parentNode->degress++] = broKey;
			_insert_shellMode_Sort(parentNode, broKey);//父节点的键值进行排序
			findNode->key[findNode->degress++] = parKey;
			_insert_shellMode_Sort(findNode, parKey);
			fdndResetConnect(findNode, addr);
			break;
		}
	}

有关于沉底的操作,在这里就不提供代码了。

感谢您的仔细阅读。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值