【概述】
二叉排序树(Binary Search Tree),又称二叉查找树,其本质是按一定的顺序进行构造的二叉树。
其递归定义如下:
- 若其左子树不空,则左子树上所有结点的值均小于根结点的值
- 若其右子树不空,则右子树上所有结点的值均大于根节点的值
- 其左右子树也是二叉排序树
从上述定义可以看出,二叉排序树是记录之间满足一定次序的二叉树,中序遍历二叉排序树可以得到一个按关键码有序的序列。
折半查找判定树就是一棵二叉排序树:点击这里
【实现类】
template<class T>
struct Node{
T data;
Node *lchild,*rchild;
};
template<class T>
class BiSortTree{
public:
BiSortTree(T a[],int n);//构造函数
~BiSortTree(){release(root);}//析构函数
void insertBST(Node<T> *&root,Node<T> *s);//插入结点
void deleteBST(Node<T> *p,Node<T> *f);//删除结点
Node<T> *searchBST(Node<T> *bt,int k);//查找结点
private:
Node<T> *root;//指向根节点的头指针
void release(Node<T> *bt);//析构函数调用
};
【插入操作】
根据二叉排序树的定义,向二叉排序树中插入一个结点 s 的过程可用如下伪代码描述:
- 若 root 是空树,则将结点 s 作为根节点插入
- 否则,若 s->data 小于 root->data,则将结点 s 插入到 root 的左子树中
- 否则,将结点 s 插入到 root 的右子树中
从上述插入过程可以看出,插入的结点是作为叶结点插入到二叉排序树中的,其过程是递归进行的。
template<class T>
void BiSortTree<T>::insertBST(Node<T> *&root,Node<T> *s) {
if(root==NULL)
root=s;
else if(s->data<root->data)
insertBST(root->lchild,s);
else
insertBST(root->rchild,s);
}
【构造函数】
构造二叉排序树的过程是从空的二叉排序树开始,依次插入一个个结点,其构造过程可用如下伪代码描述:
依次取每一个记录 a[i],进行如下操作:
1)申请一个数据域为 a[i] 的结点 s,令结点 s 的左右指针域为空
2)调用插入操作的方法,将结点 s 插入二叉排序树中
template<class T>
BiSortTree<T>::BiSortTree(T a[],int n){
for(int i=0;i<n;i++){
Node<T> *s;
s->data=a[i];
s->lchild=NULL;
s->rchild=NULL;
insertBST(root,s);
}
}
【删除操作】
在二叉排序树中,由于删除结点时,删除的可能是叶结点也可能是分支结点,因此在删除时,需要重新修改指针,使得删除一个结点后,仍能保证二叉排序树的特性。
在二叉排序树中的删除操作中,设待删除结点为 p,其父结点为 f,且 p 是 f 的左孩子,那么可分为以下三种情况进行讨论:
1.p 为叶结点
当被删除的结点为叶结点时,由于删除后不影响二叉排序树特性,因此只需将被删除结点的父节点 f 的相应指针域改为空即可。
如下图,有:f->lchild=NULL
2.p 只有左子树 pl 或只有右子树 pr
当被删除的结点仅有一棵子树时,需要将 pl 或 pr 替换为 f 的左子树。
如下图,有:f->lchild=p->lchild 或 f->rchild=p-rchild
3.p 既有左子树又有右子树
当被删除的结点有两棵子树时,需要找其前驱(即:大于 p 的最小值 或 小于 p 的最大值)来将其代替,然后删除该前驱。
综上,删除操作可用如下伪代码描述:
1.若结点 p 是叶结点,则直接删除结点 p
2.若结点 p 只有左子树,则只需重接 p 的左子树;若结点 p 只有右子树,则只需重接 p 的右子树
3.若结点 p 的左右子树均不空,则:
1)查找结点 p 的右子树上的最左下结点 s 及 s 双亲结点 f
2)将结点 s 数据域替换到被删结点 p 的数据域
3)若结点 p 的右孩子无左子树,则将 s 的右子树接到 f 的右子树上;否则,将 s 的右子树接到结点 f 的左子树上
4)删除结点 s
template<class T>
void BiSortTree<T>::deleteBST(Node<T> *p,Node<T> *f){
if(p->lchild==NULL && p->rchild==NULL){//p是叶结点
if(f->child==p)
f->lchild=NULL;
else
f->lchild=NULL;
delete p;
}
else if(p->rchild==NULL){//p只有左子树
if(f->child==p)
f->lchild=p->lchild;
else
f->rchild=p->lchild;
delete p;
}
else if(p->lchild==NULL){//p只有右子树
if(f->child==p)
f->lchild=p->rchild;
else
f->rchild=p->rchild;
delete p;
}
else{//左右子树均不空
f=p;
Node<T> *s=p->rchild;
while(s->lchild!=NULL){//查找最左下结点
f=s;
s=s->lchild;
}
p->data=s->data;
if(f==p)//特殊情况
p->rchild=s->rchild;
else//一般情况
f->lchild=s->rchild;
delete s;
}
}
【查找操作】
由于二叉排序树的定义,在二叉树中查找给定值 k 的过程是:
- 若 root 是空树,查找失败
- 若 k=root->data,查找成功
- 若 k<root->data,在 root 的左子树进行查找
- 若 k>root->data,在 root 的右子树进行查找
上述过程一直持续到 k 被找到或者待查找的子树为空,若待查找的子树为空,则查找失败。值得注意的是,当查找失败时,恰好找到了以 k 为键值的新结点在二叉排序树中的插入位置。
template<class T>
Node<T> *BiSortTree<T>::searchBST(Node<T> *bt,int k){
if(bt==NULL)
return NULL;
else if(bt->data==k)
return bt;
else if(k<bt->data)
return searchBST(bt->lchild,k);
else
return searchBST(bt->rchild,k);
}
【析构函数】
二叉排序树的析构函数与二叉链表的析构函数相同
template<class T>
void BiSortTree<T>::release(Node<T> *bt){
if(bt!=NULL){
release(bt->lchild);
release(bt->rchild);
delete bt;
}
}