二叉排序树的定义——
二叉排序树 ( Binary Sort Tree) 或者为空;或者是具有如下特性的二叉树:
(1)若根的左子树不空,则左子树上所有结点的关键字均小于根结点的关键字;
(2)若根的右子树不空,则右子树上所有结点的关键字均大于根结点的关键字;
(3)根的左、右子树也分别是二叉排序树。
一棵二叉排序树的中序遍历结果:关键字按从小到大的顺序排列。
二叉排序树的查找——
若二叉排序树为空,则查找不成功;否则
1)若给定值等于根结点的关键字,则查找 成功;
2)若给定值小于根结点的关键字,则继续在左子树上进行查找;
3)若给定值大于根结点的关键字,则继续在右子树上进行查找。
// 二叉树的二叉链表结点结构定义
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 ); // 在右子树继续查找
}
}
二叉排序树的插入——
若二叉排序树为空,则新结点做根,否则从根出发进行查找。
查找成功时空操作,查找不成功时将新结点作为查找路径上最后一个结点的左孩子或右孩子。
新结点总是作为二叉排序树的叶子。
二叉排序树的插入操作之中包含着一个查找操作,该查找操作应具备两项功能:
1)确定要插入的关键字 key 在二叉排序树中是否存在;
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; // 树中已有关键字相同的结点,不再插入
}
}
二叉排序树的删除——
二叉排序树中结点的删除操作只能在查找成功的前提下进行,并且在二叉排序树上删除某个结点之后,应使其保持二叉排序树的特性。
删除方法可分三种情况讨论:
(1)被删除的结点是叶子;——其双亲结点中相应指针域的值改为“空”
(2)被删除的结点只有左子树或只有右子树;—— 其双亲结点的相应指针域的值改为指向被删除结点的左子树或右子树的根。
(3)被删除的结点既有左子树,也有右子树。——以其前驱替代之,然后再删除该前驱结点
Status DeleteBST(BiTree *T, int key)
{
if( !*T )
{
return FALSE;
}
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 *p)
{
//q用来存储双亲结点
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;
if( q != *p )
{
q->rchild = s->lchild;
}
else
{
q->lchild = s->lchild;
}
free(s);
}
return TRUE;
}
二叉排序树查找性能的分析——
由于新插入的结点总是做叶子,所以二叉排序树的形态与关键字的插入顺序有关。
由值相同的 n 个关键字,构造所得的不同形态的各棵二叉排序树的平均查找长度的值不同,甚至可能差别很大。