查找算法【二叉查找树】 - 二叉查找树的删除

本文详细介绍了二叉查找树的删除算法。先在树中找到待删除节点,根据其左右子树情况分三种处理:左子树为空,右子树代替;右子树为空,左子树代替;左右子树均不为空,用直接前驱或后继代替。还给出算法步骤、示例及复杂度分析,时间复杂度为O(logn)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

查找算法【二叉查找树】 - 二叉查找树的删除

首先要在二叉查找树中找到待删除节点,然后执行删除操作。

假设指针p 指向待删除节点,指针f 指向p 的双亲节点。根据待删除节点所在位置的不同,删除操作的处理方法也不同,可分为下面三种情况。

① 被删除节点的左子树为空。如果被删除节点的左子树为空,则令其右子树子承父业代替其位置即可。

例如,在二叉查找树中删除P节点,如下图所示。

在这里插入图片描述

② 被删除节点的右子树为空。

如果被删除节点的右子树为空,则令其左子树子承父业代替其位置即可,如下图所示。

在这里插入图片描述

③ 被删除节点的左右子树均不为空。

如果被删除节点的左子树和右子树均不为空,则无法再使用子承父业的方法了。根据二叉查找树的中序有序性,删除该节点时,可以用其直接前驱(或直接后继)代替其位置,然后删除其直接前驱(或直接后继)即可。

那么在中序遍历序列中,一个节点的直接前驱(或直接后继)是哪个节点呢?

直接前驱: 在中序遍历中,节点P 的直接前驱为其左子树的最右节点。即沿着P 的左子树一直访问其右子树,直到没有右子树,就找到了最右节点,如下图(a )所示。

直接后继: 在中序遍历中,节点P 的直接后继为其右子树的最左节点。如图(b )所示,s 指向p 的直接后继,q 指向s 的双亲。f 、p、q、s 为指向节点的指针,也可以代指该节点。

在这里插入图片描述

以p 的直接前驱s 代替p 为例,相当于把s 的数据赋值给p ,即s代替p ,然后删除s 即可,因为s 为最右节点,它没有右子树,删除后,左子树子承父业代替s ,如下图所示。

在这里插入图片描述

例如,在二叉查找树中删除24。

首先查找到24的位置p ,然后找到p 的直接前驱s (22),把22赋值给p 的数据域,删除s ,删除过程如下图所示。

在这里插入图片描述

删除节点之后是不是仍然满足二叉查找树的中序遍历有序性?

需要注意的是,有一种特殊情况,即p 的左孩子没有右子树,s 就是其左子树的最右节点(直接前驱),即s 代替p ,然后删除s 即可,因为s 为最右节点且没有右子树,删除后,左子树子承父业代替s 。如下图所示。

在这里插入图片描述

例如,在二叉查找树中删除20,删除过程如下图所示。

在这里插入图片描述

【算法步骤】

① 在二叉查找树中查找待删除关键字的位置,p 指向待删除节点,f 指向p 的双亲节点,如果查找失败,则返回。

② 如果查找成功,则分三种情况进行删除操作。

  • 如果被删除节点的左子树为空,则令其右子树子承父业代替其位置即可。
  • 如果被删除节点的右子树为空,则令其左子树子承父业代替其位置即可。
  • 如果被删除节点的左右子树均不为空,则令其直接前驱(或直接后继)代替它,再删除其直接前驱(或直接后继)。

【举个栗子】

① 左子树为空。在二叉查找树中删除32,首先查找到32所在的位置,判断其左子树为空,则令其右子树子承父业代替其位置。删除过程如下图所示。

在这里插入图片描述

② 右子树为空。在二叉查找树中删除69,首先查找到69所在的位置,判断其右子树为空,则令其左子树子承父业代替其位置。删除过程如下图所示。

在这里插入图片描述

③ 左右子树为不空。在二叉查找树中删除25,首先查找到25所在的位置,判断其左右子树均不为空,则令其其直接前驱(左子树最右节点20)代替它,再删除其直接前驱20即可。删除20时,其左子树代替其位置。删除过程如下图所示。

在这里插入图片描述

【算法实现】

void DeleteBST(BSTree &T , char key){ 
	//从二叉查找树T中删除关键字等于key 的节点
	BSTree p = T;
	BSTree f = NULL;
	BSTree q , s;
	
	if(!T){
		return;  //树为空则返回
	}
	while(p){ //查找
		if(p->data == key){
			break;  //找打关键字等于key的节点p,结束循环
		}
		f = p;  //f为p 的双亲
		if(p->data > key){
			p = p->lchild;  //在p的左子树中继续查找
		}
		else{
			p = p->rchild; //在p 的右子树中继续查找
		}
	}
	if(!p){
		return; //找不到被删除节点则返回
	}

	//三种情况:p左右子树均不为空、无右子树、无左子树
	if((p->lchild) && (p->rchild)){ //被删除节点p 的左右子树均不为空
		q = p;
		s = p->lchild;
		while(s->lchild){ //在p 的左子树中查找p的前驱s,即最右下节点
			q = s;
			s = s->rchild;
		}
		p->data = s->data;  //将s 的值赋给被删除节点p,然后删除s
		if(q != p){
			q->rchild = s->lchild; //重接q 的右子树
		}
		else{
			q->lchild = s->lchild; //重接q 的左子树
		}
		delete s;
	}
	else{
		if(!p->rchild){ //被删除节点p 无右子树,只需重接其左子树
			q = p;
			p = p->lchild;
		}
		else if(!p->lchild){ //被删除节点p 无左子树,只需重接其右子树
			q = p;
			p = p->rchild;
		}
		
		//将p所指的子树挂接到其双亲f 相应的位置
		if(!f){
			T = p; //被删除节点为根节点
		}
		else if(q == f->lchild){
			f->lchild = p; //挂接到f 的左子树位置
		}
		else{
			f->rchild = p; //挂接到f 的右子树位置
		}
		delete q;
	}
}

【算法分析】

二叉查找树的删除主要是查找的过程,需要O (logn )时间。在删除过程中,如果需要查找被删除节点的前驱,则也需要O(logn )时间。所以,在二叉查找树中进行删除操作的时间复杂度为O(logn )。

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

祝我天天开心,平安健康

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

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

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

打赏作者

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

抵扣说明:

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

余额充值