什么是二叉搜索树
二叉搜索树(BST,Binary Search Tree), 也称二叉排序树或二叉查找树 。一棵二叉树,可以为空;如果不为空,满足以下性质:
- 非空左子树的所有键值小于其根结点的键值(/关键字)。
- 非空右子树的所有键值大于其根结点的键值。
- 左、右子树都是二叉搜索树。
- 注意:二叉排序树中没有相同关键字的结点。对二叉搜索树进行中序遍历可以得到按关键字排序的有序序列。*
//在讨论二叉排序树上的运算之前,定义其节点的类型如下:
typedef int keyType
typedef struct node //记录类型
{ KeyType key; //关键字项
InfoType data; //其他数据域
struct node *lchild,*rchild; //左右孩子指针
}
BSTNode;
二叉搜索树的指定结点的查找
顾名思义,这种二叉树就是用来查找的。
因为二叉排序树可看做是一个有序表,所以在二叉排序树上进行查找,和二分查找类似,也是一个逐步缩小查找范围的过程。
查找从根结点开始,如果树为空,返回NULL 。
若搜索树非空,则根结点关键字和X进行比较,并进行不同处理:
若X小于根结点键值,只需在左子树中继续搜索;如果X大于根结点的键值,在右子树中进行继续搜索; 若两者比较结果是相等,搜索完成,返回指向此结点的指针。
//递归
BSTNode *SearchBST(BSTNode *bt,KeyType k)
{ if(bt==NULL || bt->key==k) //递归终结条件
return bt;
if (k<bt->key)
return SearchBST(bt->lchild,k); //在左子树中递归查找
else return SearchBST(bt->rchild,k); //在右子树中递归查找
}
由于非递归函数的执行效率高,可将“尾递归”函数改为迭代函数 :
//非递归
BSTNode *SearchBST1(BSTNode *bt,KeyType k)
{
while (bt!=NULL)
{
if (k==bt->key)
return bt;
else if (k<bt->key)
bt=bt->lchild; //在左子树中递归查找
else
bt=bt->rchild; //在左子树中递归查找
}
return NULL; //没有找到返回NULL
}
注:查找的效率决定于树的高度
查找最大和最小元素
按照性质,最大元素一定是在树的最右分枝的端结点上 ;最小元素一定是在树的最左分枝的端结点上 。参考代码如下,按英文意思进行理解即可:
//查找最小元素的递归函数
Position FindMin( BinTree BST )
{
if( !BST )
return NULL; /*空的二叉搜索树,返回NULL*/
else if( !BST->Left ) return BST; /*找到最左叶结点并返回*/
else return FindMin( BST->Left ); /*沿左分支继续查找*/ }
//查找最大元素的迭代函数
Position FindMax( BinTree BST )
{
if(BST)
while( BST->Right )
BST = BST->Right; /*沿右分支继续查找,直到最右叶结点*/
return BST;
}
二叉搜索树的插入
关键是要找到元素应该插入的位置。
插入过程:
(1)若二叉排序树T为空,则创建一个key域为k的节点,将它作为根节点;
(2)否则将k和根节点的关键字比较,若两者相等,则说明树中已有此关键字k,无须插入,直接返回0;
(3)若kkey,则将k插入根节点的左子树中。
(4)否则将它插入右子树
int InsertBST(BSTNode *&p,KeyType k) //在以*p为根节点的BST中插入一个关键字为k的节点。插入成功返回1,否则返回0
{
if(p==NULL) //原树为空, 新插入的记录为根节点
{
p=(BSTNode*)malloc(sizeof(BSTNode));
p->key=k;
p->lchild=p->rchild=NULL;
return 1;
}
else if (k==p->key) //存在相同关键字的节点,返回0
return 0;
else if (k<p->key) return InsertBST(p->lchild,k); //插入到左子树中
else return InsertBST(p->rchild,k); //插入到右子树中
}
二叉搜索树的生成
二叉排序树的生成,是从一个空树开始,每插入一个关键字,就调用一次插入算法将它插入到当前已生成的二叉排序树中。
BSTNode *CreatBST(KeyType A[],int n) //返回树根指针
{
BSTNode *bt=NULL; //初始时bt为空树
int i=0;
while (i<n)
{
InsertBST(bt,A[i]); //将A[i]插入二叉排序树T中
i++;
}
return bt; //返回建立的二叉排序树的根指针
}
任何节点插入到二叉排序树时,都是以叶子节点插入的。
**二叉搜索树的删除 **
这个过程是最复杂的。
1.①被删除的节点是叶子节点:直接删去该节点。其双亲节点中相应指针域的值改为“空”
②被删除的节点只有左子树或者只有右子树,用其左子树或者右子树代替它。其双亲节点的相应指针域的值改为 “指向被删除节点的左子树或右子树”。
③被删除的节点既有左子树,也有右子树:以其前驱替代之,然后再删除该前驱节点。前驱是左子树中最大的节点
int DeleteBST(BSTNode *&bt,KeyType k) //在bt中删除关键字为k的节点
{
if(bt==NULL) return 0; //空树删除失败
else
{
if (k<bt->key)
return DeleteBST(bt->lchild,k);//递归在左子树中删除为k的节点
else if (k>bt->key) return DeleteBST(bt->rchild,k);//递归在右子树中删除为k的节点
else
{
Delete(bt); //调用Delete(bt)函数删除*bt节点
return 1;
}
}}
void Delete(BSTNode *&p) //从二叉排序树中删除*p节点
{
BSTNode *q;
if(p->rchild==NULL) //*p节点没有右子树的情况
{
q=p;
p=p->lchild; //其左子树的根节点放在被删节点的位置上
free(q);
}
else if (p->lchild==NULL) //*p节点没有左子树
{
q=p;
p=p->rchild; //其右子树的根节点放在被删节点的位置
free(q);
}
else Delete1(p,p->lchild); //*p节点既有左子树又有右子树的情况
}
void Delete1(BSTNode *p,BSTNode *&r) //当被删*p节点有左右子树时的删除过程
{
BSTNode *q;
if (r->rchild!=NULL) Delete1(p,r->rchild); //递归找最右下节点
else //找到了最右下节点*r
{
p->key=r->key; //将*r的关键字值赋给*p
q=r;
r=r->lchild; //将左子树的根节点放在被删节点的位置上
free(q); //释放原*r的空间
}
}
2.另一种类似做法:
考虑三种情况:
①要删除的是叶结点:直接删除,并再修改其父结点指针—置为NULL ;
②要删除的结点只有一个孩子结点: 将其父结点的指针指向要删除结点的孩子结点 ;
③要删除的结点有左、右两棵子树: 用另一结点替代被删除结点:右子树的最小元素 或者 左子树的最大元素 ;取右子树中的最小元素替代 ,取左子树中的最大元素替代 ;
BinTree Delete( ElementType X, BinTree BST )
{
Position Tmp;
if( !BST )
printf("要删除的元素未找到");
else if( X < BST->Data )
BST->Left = Delete( X, BST->Left); /* 左子树递归删除 */
else if( X > BST->Data )
BST->Right = Delete( X, BST->Right); /* 右子树递归删除 */
else /*找到要删除的结点 */
if( BST->Left && BST->Right )
{ /*被删除结点有左右两个子结点 */
Tmp = FindMin( BST->Right ); /*在右子树中找最小的元素填充删除结点*/
BST->Data = Tmp->Data;
BST->Right = Delete( BST->Data, BST->Right); /*在删除结点的右子树中删除最小元素*/
}
else { /*被删除结点有一个或无子结点*/
Tmp = BST;
if( !BST->Left ) /* 有右孩子或无子结点*/
BST = BST->Right;
else if( !BST->Right ) /*有左孩子或无子结点*/
BST = BST->Left;
free( Tmp );
}
return BST;
}
二叉查找树结构由于树的深度过大而造成磁盘I/O读写过于频繁,进而导致查询效率低下.那么如何提高效率,即如何避免磁盘过于频繁的多次查找呢?根据磁盘查找存取的次数往往由树的高度所决定,所以,只要我们通过某种较好的树结构减少树的结构尽量减少树的高度。这样的树就是:平衡二叉树