二叉排序树(Binary sort tree),又称二叉查找树。它或者是一颗空树,或者是具有下列性质的二叉树:
1.若它的左子树不空,则左子树上所有结点的值均小于它的根结构的值
2.若它的右子树不空,则右子树上所有结点的值均大于它的根结构的值
3.它的左、右子树也分别为二叉排序树
二叉排序树的结点间满足一定的次序关系,左子树结点一定比其双亲结点小,右子树结点一定比其双亲结点大。
1.二叉排序树查找操作
首先二叉树的结构如下:
typedef struct BiTNode //结点结构
{
int data; //结点数据
struct BiTNode *lchild, *rchild; //左右孩子指针
} BiTNode, *BiTree;
查找操作如下:
/* 递归查找二叉排序树T中是否存在key,
指针f指向T的双亲,其初始调用值为NULL.
若查找成功,则指针P指向该数据元素结点,并返回TRUE;
否则指针p指向查找路径上访问的最后一个结点并返回FALSE
*/
Status SearchBST(BiTree T, int key, BiTree f, BiTree *p)
{
if(!T) //查找不成功
{
*p = f;
return FALSE;
}
else if(key == T->data) //查找成功
{
*p = T;
return TRUE;
}
else if(key<T->data)
return SearchBST(T->lchild, key, T, p) //在左子树继续查找,递归调用
else
return SearchBST(T->rchild, key, T, p) //在右子树继续查找,递归调用
}
2.二叉排序树插入操作
二叉排序树的插入,其实就是将关键字放到树中的合适位置。代码如下:
/* 当二叉排序树T中不存在关键字等于key的数据元素时
插入key并返回TRUE,否则返回FALSE
*/
Status InsertBST(BiTree *T, int key)
{
BiTree p,s;
if(!SearchBST(*T, key, NULL, &p))
{
s = (BiTree)malloc(sizeof(BiTNode));
s->data = key;
s->lchild = s->rchild = NULL;
if(!p)
*T = s; //插入s为新的根结点
else if (key <p->data)
p->lchild = s ; //插入s为左孩子
else
p->rchild = s; //插入s为右孩子
return TRUE;
}
else
return FALSE; //树中有与关键字相同的结点,不再插入
}
有了二叉排序树的插入代码就可以构建二叉排序树了。
int i;
int a[10] = {62,88,58,47,35,73,51,99,37,93}
BiTree = NULL;
for(i=0;i<10;i++)
{
InsertBST( &T, a[i]);
}
3.二叉排序树删除操作
删除结点存在三种情况:
1.叶子结点;
2.仅有左或者右子树的结点;
3.左右子树都有结点
下面看具体代码:
/* 若二叉排序树T中存在关键字等于key的元素时,则删除该数据元素结点
然后返回TRUE,否则返回FALSE
*/
Status DeleteBST(BiTree *T, int key)
{
if(!T) //如果不存在关键字等于key的数据元素
return FALSE;
else
{
if (key==(*T)->data) //找到关键字等于key的数据元素
return Delete(T);
else if (key<(*T)->data)
return DeleteBST(&(*T)->lchild, key);
else
return DeleteBST(&(*T)->rchild, key);
}
}
这段代码和前面的二叉排序树查找几乎完全相同,区别在于此处使用的是Delete方法。DeleteBST()函数具体方法如下:
/* 从二叉排序树中删除结点p, 并重接它左或者右子树*/
Status Delete(BiTree *p)
{
BiTree q,s;
if ((*p)->rchild = NULL) //右子树空则只需重接它的左子树
{
q=*p; *p = (*p)->lchild; free(q);
}
else if ((*p)->lchild = NULL) //左子树空则只需重接它的右子树
{
q=*p; *p = (*p)->rchild; free(q);
}
else //左右子树均不空
{
q=*p; s = (*p)->lchild;
while(s->rchild) //转左,然后向右到尽头(找到待删结点的前驱)
{
q=s; s=s->rchild;
}
(*p)->data = s->data; //s指向被删结点的直接前驱
if(q!=*p)
q->rchild = s->lchild; //重接q的右子树
else
q->lchild = s->rchild; //重接q的左子树
free(s);
}
return TRUE;
}
上面的代码是删除结点的前驱结点替换的方法,对于后继结点的替换是一样的。
二叉排序树是以链接的方式存储,保持了链式结构在执行插入、删除操作时不用移动数据的优点。但二叉排序树的查找性能取决于二叉排序树的形状。问题在于,二叉排序树的形状是不确定的。因此如何是二叉排序树平衡是一个问题。