C++ 对查找树ADT实现代码的解释(笔记)

刚开始学查找树,对于前面所说的Find,MakeEmpty等函数都没太大问题,就是最后的delete函数费了好一会才理解它这样写的理由(其实一开始自己写的时候,我并没有仔细区看书上的实现代码,而是大概了解了一下过程,然后打算自己去写,结果耗费了很长一段时间也没成功,觉得有些诡异,然后才开始细细的一步步去推演代码的发展过程,姑且算是搞清楚是个怎么回事了)

这里只会对最后的delete函数做解释,其他函数都不是很难,大致看一下递归代码马上就能明白。

 

先丢一下定义:

查找树ADT——二叉查找树的性质:对于树中每一个节点X,其左子树中的所有关键字数值都小于X的关键字数值,而右子树则反之。

首先是代码:

searchtree deletenode(int x,searchtree T)
{
	position tmp;
	if (T == NULL)
	{
		exit;
	}
	else if (x < T->info)//往左
	{
		T->left = deletenode(x, T->left);
	}
	else if (x > T->info)//往右
	{
		T->right = deletenode(x, T->right);
	}
	else if (T->left && T->right)
	{
		tmp = findm(T->right, 0);
		T->info = tmp->info;
		T->right = deletenode(x, T->right);
	}
	else
	{
		tmp = T;
		if (T->left = NULL)
		{
			T = T->right;
		}
		else if(T->right=NULL)
		{
			T = T->left;
		}
		delete(tmp);
	}
	return T;
}

 这长长的一串代码,当时看的时候不太乐意,就没去推演是个什么回事,结果就遇上问题了。

显然,最上面的几行是用来查找关键字的,我最初还在想,为什么前面写了find函数不用,一定要在这里又弄一遍。多建立一个tmp用来保存find函数的返回值不就好了吗?于是就被打脸了......

注:

1. 我并没有像书上那样写了两个findmax和findmin,代码第18行的另外一个参数‘0’用来表示本次查找的目标是最小值,‘1’则代表最大值

2.代码中的info指代关键字数值

过程:

首先,我们向函数中投入关键字数值和根地址,通过最开始的递归,我们到达了我们期望删除的节点T1,并且也建立了一个tmp1,然后开始对T1节点进行判断:它是否有两个儿子(这里我们姑且假设它有两个儿子吧)。

于是我们将tmp1指向T1右支中关键值最小的那一个,并将它的关键值赋予T1(如果还有其他内容物,一并赋予就行了)

然后执行:

		T->right = deletenode(x, T->right);

我最开始的写法之所以不行,大概就是没能好好理解这句话的意思。

我们令T1节点的右儿子等于了T->right = deletenode(x, T->right)的返回值。

于是我们再次进入下一次递归,又找了一次T1节点右支中关键值最小的那一个节点,但注意,我们是用递归的方式去寻找,而不是用已经写好的findmin去找(尽管findmin也是用递归找的......)

于是我们终于找到了另外一个节点T2,它的数值和T1是一

样的,并且我们在这一轮是递归里也新建了一个tmp2。

于是程序开始判断这个T2有没有儿子。因为T2已经是T1右支中最小的儿子了,根据ADT的性质,它是不可能有左儿子的,所以T2要么有一个右儿子,要么就是没有儿子(这里我们姑且假设T2是有一个右儿子吧)。

那么这一轮的递归无疑会进行到:

tmp=T

 也就是让tmp2=T2。

然后又开始判断T2是否有左儿子,根据假设,我们让T2成为了它的右儿子,并将它本来的地方,也就是被暂时保存下来的tmp2给释放了。

最后我们将T2返回,也就是返回了它本来的右儿子的地址。

还记得最开始的递归查找吗?当我们成功在这所有递归中返回了第一个地址,递归开始回滚,我们将会回到

	else if (x < T->info)//往左
	{
		T->left = deletenode(x, T->left);
	}

 

假设我们要查找关键值‘2’,当我们进入5之后,我们将会继续向左,进入‘2’节点,也就是上面的T2,在这里开始第一次返回,于是我们进入了关键值为‘5’的节点T3。

我们返回的其实是关键值为‘4’的地址,于是这个‘5’号节点的左儿子就变成了‘4’,并且‘2’节点也不存在了。然后我们继续向下,将‘5’号节点的地址返回,重复起了上述的操作。

 

对于那些没经过变换的节点,我们的返回值其实和原本的值是一样的,比如五节点向上返回,只是让上面的节点又等于了一次‘5’节点罢了。

 

 

最开始的问题:

void deletetest(int x, searchtree T)
{
	position tmp1,tmp2;
	tmp1=find(x, T);
	tmp2=findm(tmp1->right, 0);
	tmp1->info = tmp2->info;
}

想的很简单,把关键值节点找出来,再把右支最小节点找出来,一赋值,再把最小那个删掉就行了。但我还没写delete就发现,我没办法把最小值的上层节点的指针指向NULL,直接删除的话很容易出现各种各样的问题。

但也不是说毫无思路,或许我们可以给每个节点都添加一个‘指向父级的指针’,这样就能实现正序和反序的查找了。当我们要删除某个节点,也只需要把下面的节点和上面的节点连接起来,把自身删除就行了。这样一想,或许和之前的链表差不多,就是不知道这样写查找树还有没有意义,算不算是偏离了查找树的本意......

 

 

猜你喜欢

转载自blog.csdn.net/Tokameine/article/details/113315090