二叉排序树(查找、插入、删除)

1.二叉排序树

二叉排序树又称二叉查找数,它或者是一棵空树,或者是具有下列性质的二叉树

1.若它的左子树不为空,则左子树上所有结点的值均小于它的根结构的值

2.若它的右子树不为空,则右子树上所有结点的值均大于它的根结构的值

3.它的左右子树也分别为二叉排序树(递归)

比如现在有一个无序序列

《二叉排序树(查找、插入、删除)》

想要生成一个二叉排序树,首先将70 作为根节点 

《二叉排序树(查找、插入、删除)》

将105作为70 的右子树,

《二叉排序树(查找、插入、删除)》

115>70,115>105,作为105的右子树

《二叉排序树(查找、插入、删除)》

104>70,104<105,作为105的左子树

《二叉排序树(查找、插入、删除)》

依次类推:

《二叉排序树(查找、插入、删除)》《二叉排序树(查找、插入、删除)》

《二叉排序树(查找、插入、删除)》

《二叉排序树(查找、插入、删除)》

《二叉排序树(查找、插入、删除)》

2.二叉排序树的查找

利用递归的方式,如果key 大于当前结点,则继续查找当前结点的右子树,如果小于,则继续查找当前结点的左子树。如果最后没有找到,则查找失败。

typedef int Status;
#define OK 1
#define ERROR 0
typedef struct BiTNode
{
	int data;
	struct BiTNode *lchild;
	struct BiTNode *rchild;
}BiTNode,*BiTree;

// 递归查找二叉排序树 T 中是否存在 key
// 指针 f 指向 T 的双亲,其初始值调用值为 NULL
// 若查找成功,则指针 p 指向该数据元素结点,并返回 OK
// 否则指针 p 指向查找路径上访问的最后一个结点,并返回 ERROR

Status SearchBiTree(BiTree T, int key,BiTree f,BiTree *p)
{
	//空树
	if (!T)
	{
		//说明没有查到,返回访问路径上的最后一个节点,也即是T的双亲
		*p = f;
		return ERROR;
	}
	else if (T->data == key)
	{
		//找到了
		*p = T;//成功则p指向查找到的节点
		return OK;
	}
	else if (T->data > key)
	{
		return SearchBiTree(T->lchild, key, f, p);//在左子树继续查找
	}
	else
		return SearchBiTree(T->rchild, key, f, p);//在右子树继续查找
}

3.二叉排序树的插入

首先判断是否已经存在该元素,如果已经存在则插入操作失败,否则插入。

利用二叉排序树的查找操作,如果最后没有找到该元素,由于传入了一个指针指向找到的元素结点(找到时)或者查找路径上的最后一个结点(没有找到时),所以如果没有找到,就将待插元素与查找路径上的最后一个结点的值进行比较,小于则作为该结点的左子树,大于则作为该结点的右子树。

// 当二叉排序树 T 中不存在关键字等于 key 的数据元素时,
// 插入 key 并返回 OK,否则返回 ERROR
Status InsertBST(BiTree *T, int key)
{
	//首先查找是否已经存在该元素
	BiTree p;
	Status s = SearchBiTree(*T, key, NULL, &p);
	if (s == OK)
	{
		//该二叉有序树中已经存在该元素!不再插入
		return ERROR;
	}
	//不存在要插入的元素,则插入,
	else
	{
		BiTNode *r = (BiTNode*)malloc(sizeof(BiTNode));
		r->data = key;
		r->lchild = NULL;
		r->rchild = NULL;
		//p为空说明为空树,因为在查找函数中,T为空,将T的双亲f赋给p,而在传参时f=NULL;
		if (!p)
		{
			*T = r;//插入s为新的根节点
		}
		else if (key > p->data)
		{
			p->rchild = r; //插入s为右孩子
		}
		else
		{
			p->lchild = r; //插入s为左孩子
		}
		return OK;
	}
}

4.二叉排序树的删除

二叉排序树的删除操作比较麻烦,首先要找的待删元素,如果找不到,则不删除任何元素,如果找到了,有三4种情况

1.叶子结点

《二叉排序树(查找、插入、删除)》

比如我们要找的是46,直接删除即可

2.只有左子树

《二叉排序树(查找、插入、删除)》

如果我么要删除67,67只有左子树,我们将67的左子树接到67的双亲,再讲67删除即可

3.只有右子树

《二叉排序树(查找、插入、删除)》

4.既有左子树又有右子树

比如上图我们想删除105,与只有左子树类似,只需将105的左子树接到105的双亲,删除105即可。

《二叉排序树(查找、插入、删除)》

还是删除105,但是105现在既有左子树又有右子树,直接将左子树或右子树接到105的双亲都不对,你把左子树接到双亲,右子树怎么办,同样的你把右子树接到双亲左子树怎么办。

这里需要提到二叉排序树的一个特点,二叉排序树用中序遍历是有序的,如上图中序遍历的结果是 46,67,70,99,100,103,104,105,108,110,112,115。

解决二叉树删除问题的第一种方式,只要将待删除结点的数据替换为其直接前驱。

并将直接前驱的左子树(一定是左子树,否则该点一定不是直接前驱,可以回想一下中序遍历的过程,左-根-右,不明白可以给104加一个右子树)接到双亲,删除该直接前驱结点。

《二叉排序树(查找、插入、删除)》

还有一种情况就是其直接后继就是其左子树,也就是其左子树不存在右子树

《二叉排序树(查找、插入、删除)》

此时只需要将直接后继的左子树接为待删数据的左子树即可

《二叉排序树(查找、插入、删除)》

 

第二种方式是将该结点的数据替换为直接后继。

将直接后继的右子树(一定为右子树,否则该节点不是直接后继,不明白可以给108加左子树,用中序遍历走一遍)接到直接后继的双亲,删除直接后继结点

《二叉排序树(查找、插入、删除)》

还有一种情况与用直接前驱一样,其直接后继结点就是其右子树

《二叉排序树(查找、插入、删除)》

此时将右子树的右子树接为待删数据结点的右子树

《二叉排序树(查找、插入、删除)》

代码如下:


Status DeleteBST(BiTree *T, int key)
{
    //空树
	if (!*T)
	{
		return ERROR;
	}
	else
	{
		if (key == (*T)->data)
		{
			return Delete(T);
		}
        //继续寻找该节点
		else if (key < (*T)->data)
		{
			return DeleteBST(&(*T)->lchild, key);
		}
		else
			return DeleteBST(&(*T)->rchild, key);

	}
}


Status Delete(BiTree *T)
{
	BiTree q, s;
    //叶子结点的情况包含在只有左子树和只有右子树的情况里了
    //只有左子树
	if ((*T)->rchild == NULL)
	{
		q = *T;
		*T = (*T)->lchild;
		free(q);
	}
    //只有右子树
	else if ((*T)->lchild == NULL)
	{
		q = *T;
		*T = (*T)->rchild;
		free(q);
	}
    //有左右子树
    //该方法是利用直接前驱替换
	else
	{
		//让q=直接前驱的双亲
		q = *T;
		//寻找待删结点的直接前驱
		s = (*T)->lchild;
		while (s->rchild)
		{
			q = s;
			s = s->rchild;
		}
		//将待删数据替换为直接前驱的数据
		(*T)->data = s->data;
		if (q != *T)
		{
			//将直接前驱的左子树接到直接前驱的双亲
			q->rchild = s->lchild;
		}
		else
		{
			//如果直接前驱就是待删数据结点的左子树
			q->lchild = s->lchild;
		}
		//释放直接前驱
		free(s);
	}
	////该方法利用直接后继
	//else
	//{
	//	//让q=直接后继的双亲
	//	q = *T;
	//	s = (*T)->rchild;
	//	//寻找直接后继
	//	while (s->lchild)
	//	{
	//		q = s;
	//		s = s->lchild;
	//	}
	//	//将数据替换为直接后继
	//	(*T)->data = s->data;
	//	if (q != *T)
	//	{
	//		//将直接后继的右子树接到直接后继的双亲的左子树
	//		q->lchild = s->rchild;
	//	}
	//	else
	//	{
	//		//如果直接后继就是其右子树,将直接后继的右子树接到待删结点的右子树
	//		q->rchild = s->rchild;
	//	}
	//	//释放直接后继
	//	free(s);
	//}
	return OK;
}

 

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