理论基础 —— 查找 —— 二叉排序树

【概述】

二叉排序树(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 的过程可用如下伪代码描述:

  1. 若 root 是空树,则将结点 s 作为根节点插入
  2. 否则,若 s->data 小于 root->data,则将结点 s 插入到 root 的左子树中
  3. 否则,将结点 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;
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值