非线性数据结构 之 AVL树(2)

这一节关注AVL树的删除操作,相对于插入和查找,AVL树的删除要复杂得多。


首先,如果不考虑“平衡性”,那么删除一个节点也不难,就是BST树的思路。

一开始,在树中查找这个节点,如果找不到,那就返回。如果找到了,那么再细分三种情况:


1. 这个待删除节点有左子树,那么我们要做的就是将其左子树中最大的节点替换该节点,然后再将左子树中的那个节点删除即可(递归算法):




2. 这个待删除节点没有左子树但有右子树,那么我们要做的就是将其右子树中最小的节点替换该节点,然后再将右子树中的那个节点删除即可(递归算法):



3, 要删除的节点既没有左子树也没有右子树,是一个叶子节点。这种情况最简单,直接删除即可。


上面三种情况,代码实现如下:

linktree AVL_remove(linktree root, tn_datatype data)
{
	if(root == NULL)
		return NULL;
    // 以下是处理删除节点的代码
	if(data < root->data)
		root->lchild = AVL_remove(root->lchild, data); // 如果待删除节点比当前节点小,那么去左子树中删除
	else if(data > root->data)
		root->rchild = AVL_remove(root->rchild, data); // 如果待删除节点比当前节点大,那么去右子树中删除
	else
	{
		linktree p;

		if(root->lchild != NULL) // 1. 待删除的节点(红色)有左子树
		{
			for(p=root->lchild; p->rchild!=NULL; p=p->rchild){;} // 找到左子树中最大的节点(蓝色)
			root->data = p->data; // 替换待删除节点
			root->lchild = AVL_remove(root->lchild, p->data); // 在左子树中将该节点(蓝色)删除
		}
		else if(root->rchild != NULL) // 2. 待删除的节点没有左子树但是有右子树,以下步骤跟上面的是对称的,不敖述
		{
			for(p=root->rchild; p->lchild!=NULL; p=p->lchild){;}
			root->data = p->data;
			root->rchild = AVL_remove(root->rchild, p->data);
		}
		else // 3. 待删除的节点是叶子,直接删除。
		{
			free(root);
			return NULL;
		}
	}

	// 以下是处理平衡性的代码
	// ...
	// ...
}

节点删除了,下面要考虑平衡性的问题。情况有以下几种:

第一,删除节点之后,其左子树太高,导致树不平衡:(黑色的节点表示刚刚删除了某个节点的子树的根)


如图所示,在这种情况下,还要细分两种情况,情形一是黑色节点的左子树有左子树,这种情况下只需要将黑色节点右旋转即可恢复平衡。情形二是黑色节点的左子树没有左子树但是有右子树,这种情况下要先将其左子树左旋转,再对其进行右旋转。待会儿细述。


第二,完全对称地,删除节点后,其右子树太高,导致树不平衡:


同样,如图所示,当在子树中删除某个节点之后,可能会导致如图所示的不平衡的情况,这时其右子树太高。而两种情形的处理几乎跟上面是一样的,即情形一只需要对黑色的根节点进行一次左旋转即可,而情形二则需要先对其右孩子进行右旋转,再对其进行左旋转就可恢复平衡。


有人可能会疑问,为什么删除之后不平衡只会是这两种情况? 难道不会有其他的情况了吗? 是的没有了,上面的两种情况四种情形已经包括了所有的可能性。如果你没想明白为什么会那么简单,那是因为你没考虑这是一个递归的算法,以上情形都是发生在最后的递归的分支上。然后顺藤摸瓜层层递归,从叶子开始往上到根节点不断进行重新平衡。


以下是重新平衡的代码:

linktree AVL_remove(linktree root, tn_datatype data)
{
	if(root == NULL)
		return NULL;

	// 以下是处理删除节点的代码
	// ...
    // ...


	// 以下是处理平衡性的代码
	if(height(root->lchild) - height(root->rchild) == 2) // 左子树太高导致不平衡
	{
		if(height(root->lchild->rchild)-height(root->lchild->rchild) == 1) // 情形二
			root = LR(root);
		else // 情形一
			root = LL(root);
	}
	else if(height(root->rchild) - height(root->lchild) == 2) // 右子树太高导致不平衡
	{
		if(height(root->rchild->lchild)-height(root->rchild->rchild) == 1) // 情形二
			root = RL(root);
		else // 情形一
			root = RR(root);
	}

	root->bf = MAX(height(root->lchild), height(root->rchild)) + 1;
	return root;
}

将上述两部分代码合并起来,就是AVL完整的删除算法。

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

干燥剂007860

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值