关于二叉(元)搜索树的建立(插入、查找最大最小、删除)
在C语言中,建立二叉(元)树需要通过函数来建立二叉树。考虑到C语言会出现形参出栈直接丢弃的情况,所以这里需要考虑两种方法:
一种是在函数内进行所有操作,通过malloc函数在内存内开辟一片空间,然后将这个空间赋值给main函数里面的一个相关变量。另一种方式是导入一个指向指针的指针,对这个指向指针的指针进行相关的操作,从而对main函数中的指针进行相关操作。考虑到程序的一致性或者连贯性,我们这里通过在main函数中建立相关的指针,然后通过返回值的形式将相关地址赋值给main函数中的指针来进行二叉树的建立。
首先需要建立一个二叉树的节点
程序如下:

运用返回值的方法,程序如下:

假设我们要在下面这棵树进行相关的操作:

我们想将数字13插入到这棵树中。理论上应当插入到10的右孩子位置处
当我们导入到函数中的时候,发生了下面的事情:
首先判断T是不是空--->非空,那么比10(此时T的位置)大还是小?--->因为是大,所以调用:
T->rightNode = insert(data, T->rightNode);
此时进入另一个insert,重复上面的步骤:因为此时"T->rightNode"为NULL状态,所以进行malloc操作并进行赋值,将此时的T(T->rightNode)值返回到原函数中,这样就进行了一次插入操作.
那么,如果是下面这棵树,我们想将11插入进去呢?

重复上面的操作,此时出现了一个问题:10的右孩子不是空的。那调用函数,产生的数值赋值给了谁呢?
我们不妨用一个不是特别标准的堆栈模型来模拟这个过程:
当判断11和10的时候,因为11 > 10,所以11要到10的右边。此时10的右边已经有了13,我们先进行程序,此时又掉用了insert函数,只不过此时掉用的是11和13所在的位置T->rightNode。由前面可知,因为11<13,所以要到13的左孩子位置处,所以又掉用了一遍insert函数,此时掉用的相当于是T->right->left,那么这个过程就和往10里面插入13一样了。而后面呢?我们中间还掉用了一遍insert。不要忘了,这时候对T->right没有进行任何的修改,所以直接返回的就是T->right的数值,同理,前面也是直接返回的就是T的数值,整个过程只对最后的13的左孩子进行了相对应的插入操作,而对中间的任何树都是没有任何影响的。我们可以想到,假设这棵树很长,那么对这棵树进行先关的插入操作,同样可以很成功的插入到相对应的位置上头。
趁着热乎劲儿,我们来看一下删除函数,其特性和insert有一曲同工之妙:
首先说一下删除函数要分三种情况:
第一种情况是这个节点没有孩子,那么直接将NULL赋值给这个节点就行。
第二种情况是这个节点有一个孩子,那么将这个节点替换成这个节点的孩子
第三种情况,也就是最复杂的情况:如果这个节点包含两个孩子(在后面我们会看到这个节点下面有多少的子节点是不影响的),要将此节点右边的最小值或者左边的最大值替换掉此节点,并删除作为替换数值的这个最小(大)节点。
函数如下:

我们先看没有子节点或者有一个子节点的部分:
tempNode = T; if (T->leftNode == NULL) { T = T->rightNode; } if (T->rightNode == NULL) { T = T->leftNode; } free(tempNode);
首先是将要删除掉的节点赋值给"tempNode",如果是左边空,那么就将右边的赋值给T,反之就是将左边的赋值给T,无论是有无子节点,这一部分都是没有影响,无外乎就是将有值或者NULL赋值给T。
接下来就考虑最坑的删除有两个孩子的节点:
我们先来看一下为什么要找左边最大的或者右边最小的。
如果这棵二叉树是正规的,那么理论上对于某一节点,它的所有左子节点都应该小于它,它的所有右子节点都应该大于它。假设我们此时有这么一个树:

我们此时想要删除掉15,那么我们有两种路子可以走:一种是找15这个节点左半部分的最大值,一种是找这个节点右半部分的最小值。假设我们这回找右半部分的最小值,理论上应该是16。为什么应该是16。而不是17或者20?首先我们应当明确一个概念,对于15,它的左半部分是全部小于它的,它的右半部分是全部大于它的。而这一情况对于所有节点全部适用。那么我们想要删除掉一个节点,什么节点才是最接近这个节点的呢?自然是比它大的元素里面的最小值,或者比它小的里面的最大值。因为被删除节点还有两个孩子,所以为了孩子以及孙子的安全,我们此处找比它大的最小值16(也可以找比它小的最大值12,此处只是为了说明),肯定比“比15大的数字除了其本身小”,肯定比“比15小的数字大”。15下面的孩子是安全的,我们只需要删掉右边的16节点即可。
明白了这一概念,程序也就不难理解了:如果此节点有两个孩子,那么执行下面的部分:
if(T->leftNode != NULL && T->rightNode != NULL){ //由树的性质,我们要用左边的最大值或者右边的最小值来替换原数值。 tempNode = FindMin_recurse(T->rightNode); T->data = tempNode->data; T->rightNode = delete(T->rightNode, T->data); }
其中:
T->rightNode = delete(T->rightNode, T->data);
这一句的理解和前面insert的理解相同,在这里不再赘述。

浙公网安备 33010602011771号