首先二叉排序树,是一种插入和删除亦或者查找效率都不错的一个算法,接下来我就分享一下我在学习这个算法中的一些心得和总结。
一、二叉树的性质
一棵空树,或者是具有下列性质的二叉树:
(1)若左子树不空,则左子树上所有结点的值均小于它的根结点的值;
(2)若右子树不空,则右子树上所有结点的值均大于它的根结点的值;
(3)左、右子树也分别为二叉排序树;
------百度百科
我们通过定义就可以明白几个点
1.它本身是一个二叉树
2.它的结点都满足一定的次序关系,左子树一定比它的双亲要小,右子树一定比双亲要大
3.它进行中序遍历的时候一定是一个序列
二、二叉树的定义以及查找操作
先给出定义
typedef struct BiTNode
{
int data;
struct BiTNode* lchild;
struct BiTNode* rchild;
}*BiTree;
查找的话,先看一下它的性质, 左子树一定比它的双亲要小,右子树一定比双亲要大。所以我们这里可以基本上推算出它的思路了
1.用一个关键字key(也就是需要查找的数)和当前结点t的data进行比较
2.如果相等返回当前结点t
3.如果不相等且key小于当前结点的data的话,就进行递归查找它的左子树
4.如果不相等且key大于当前结点的data的话,就进行递归查找它的右子树
5.如果当前结点为空,也就是找不到这个数,就返回查找路径中最后一个访问的非空结点
来看一下代码
int SearchBST(BiTree T, int key, BiTree f, BiTree* p)
//f保存了上一个结点,T为当前结点,p是需要返回的
{
if (T == NULL)//当前结点为空
{
*p = f;
return 0;
}
else
if (T->data == key)//找到了
{
*p = T;
return 1;
}
else
if (T->data > key)
{
SearchBST(T->lchild, key, T, p);
}
else if (T->data < key)
{
SearchBST(T->rchild, key, T, p);
}
}
三、二叉排序树的插入操作
插入就是将关键字key放入到一个正确的位置,其实可以和上面的查找给联系起来,插入一个新的数,如果执行查找的这个函数,它会通过p返回什么?会返回它查找路径的最后一个非空结点,也就是直接插入到它的后面就行了,再判断一下为左孩子还是右孩子
下面直接给出代码
int InsertBST(BiTree* T, int key)
{
BiTree p, t;
if (SearchBST(*T, key, NULL, &p) == 0)//查找不成功
{
t = (BiTree)malloc(sizeof(BiNode));
t->data = key;
if (p == NULL)
{
*T = t;
}
else
if (key < p->data)
p->lchild = key;
else
p->rchild = key;
return 1;
}
return 0;
}
四、二叉排序数的删除操作
这个操作比较难实现,因为你要想,如果我们删除一个结点可能这颗二叉树就不满足二叉树的特性了,所以我们要考虑多种情况
1.删除的结点是叶子结点。直接删除即可。
2.删除的结点只有左子树或者只有右子树的,那就先删除结点后,再将左子树/右子树一整个搬到删除的结点的位置就行了这样子是不会影响到二叉排序数的
3.删除的结点既有左子树又有右子树的,这个时候我们就需要人来代替它当前的位置,我们上面说过,二叉排序树在进行中序遍历的时候他一定是一个序列,我现在要删除这个序列中的一个的话,我就需要找它旁边的两个结点(前驱和后继)来代替它。
所以我们大体的思路出来了找到需要删除的结点p的直接前驱(或后继)s,用s来替换结点p,再删除此节点s。
在看代码之前,我们来聊一下怎么找他的前驱,因为它中序遍历是一个序列,我们只要找离要删除的结点最近且小于它的结点就行了,就是找左子树中最右边的结点
来看代码:
int DeleteBST(BiTree* T, int key)//此函数的作用就是找到要删除的结点,和查找差不多
{
if (!*T) //不存在
return 0;
else
if ((*T)->data == key)//找到了
{
return Delete(T);
}
else
if ((*T)->data < key)
{
return DeleteBST(&(*T)->rchild, key);
}
else
if ((*T)->data > key)
{
return DeleteBST(&(*T)->lchild, key);
}
}
int Delete(BiTree* p)//这才是删除对应的主要步骤
{
BiTree q, s;
if ((*p)->lchild == NULL)
{
q = *p; *p = (*p)->rchild; free(q);
}
else
if ((*p)->rchild == NULL)
{
q = *p; *p = (*p)->lchild; free(q);
}//前面两个if对应着前一种情况
else
{
q = *p; s = (*p)->lchild;//s进入到p的左子树里去
while (s->rchild) //找最右边的结点,一直到尽头
{
q = s;
s = s->rchild;
}
(*p)->data = s->data;
if (q != *p)
q->rchild = s->lchild;//重新连接q的右子树
else
q->lchild = s->lchild;//重新连接q的左子树
free(s);
}
return 1;
}