二分搜索树的实现以及各种操作(支持重复节点)

二分搜索树

一、 二分搜索树的概念
二分搜索树就是左子树的键值小于根节点,右子树键值大于根节点的树。而且左右子树是递归定义。简单来说就是任意节点左子树的树。这里主要讨论的是二分搜索树也就是二叉树。其应用在于查找表,以及字典结构的时候。其查找速度非常之快,而且可以用其求各种算法例如max ,min ,rank 等等。
例如这就是一个简单的二分搜搜树
二分搜索树的实现以及各种操作(支持重复节点)_第1张图片

二、为什么要有搜索二叉树
最简单的查找算法就是暴力查找,从待查找序列从头遍历到尾,与查找的key进行对比。

int Search(int *Arr,int n,int key){
    for(int i=0;iif(Arr[i]==key)
            return i;
    }
    return -1;
}

这种算法简单暴力,无论查找,插入,删除操作时间复杂度都在O(n),但是这不一定是最好的。在数据量很大的情况此算法显得有点“蠢”,于是人们想到了二分查找(不是二分搜索树)。
也就是每次查找数组的mid

int biSearch(int *Arr,int n,int key){
    int l,r;
    l=0;r=n-1;
    while (l<=r) {
        int mid=l+(r-l)/2;// 防止 r,和m足够大越界问题,
        if(Arr[mid]==key){// 等于就返回其下标。
            return mid;  
        }
        else if(Arr[mid]>key){
            r=mid-1;    //在左侧进行查找
        }
        else{
            l=mid+1;   //在右侧进行查找
        }
    }
    return -1;
} 

以上代码是典型的二分查找,在查找方面时间复杂度可以降低到O(logn)级别,但是要求数组一定有序,不是有序的没有办法查找。并且其数据结构为顺序表,也就是说其插入和删除操作时间复杂度还是O(n)级别。那么有没有更优秀的算法使其搜索的时间复杂度为O(logn),插入和删除操作也为O(logn)呢,答案是肯定的,也就是今天的主角。二分搜索树。

三、二分搜索树
3.1 二分搜索树的定义
为了充分体现二分搜索树的优势和使用技巧,这里所做的二分搜索树是一个字典样式的树,也就是说其有key值,和value值。并且其用的是树的链表法去存储,所以也有节点这个概念。节点定义特别很简单,跟普通的树是一样的。

struct Node{
        int value;
        int key;
        int frequency;     // 本次做的二分搜索树是支持重复节点的,这个是记录该节点的重复次数
        Node *left;
        Node *right;
        Node(int key ,int value){
            this->value=value;
            this->key=key;
            this->left=this->right=NULL;
            this->frequency=1;
        }//构造函数
    };

为了方便操作在这里构建的一个BST的类。

class BST{
private:
    struct Node{
        int value;
        int key;
        int frequency;
        Node *left;
        Node *right;
        Node(int key ,int value){
            this->value=value;
            this->key=key;
            this->left=this->right=NULL;
            this->frequency=1;
        }//节点构造函数
    };
    Node *root;  //root为根
    int count;  // count记录的shi当前树中拥有节点的个数
public:
    BST(){
        root=NULL;
        count=0;
    } //BST树的构造函数
};

各位看客朋友请好好看看上面类的结构,接下来的所有操作都是基于上述类的。

3.2 二分搜索树的建立

其实树的建立就是一个插入过程,每次插入都不改变其树的性质和结构。

这里利用的树的天然递归性质进行插入的操作。

    Node *insert(Node *node,int key,int value){
        if(node==NULL){
            count++;
            return new Node(key,value);  //如果没有这个节点就用结构体的构造方法,new一个节点。
        }
        if(node->key< key){
            node->right=insert(node->right,key,value); //右边去看看
        }
        else if(node->key>key){
            node->left=insert(node->left,key,value); //左边去看看
        }
        else if(node->key==key)   // 因为允许重复key值的插入,所以有重复key值的时候要更新节点信息
        {
            node->value=value;  //可以不要
            node->frequency++;   // 该节点的频度要++
        }
        return node;  //返回的就是这个数的根
    }

在类的public中定义一个insert 函数,让这个二分搜索树的根传入进去即可。

    void insert(int key,int value){
        root=insert(root,key,value);
    }

大功告成,这样就能做出来一个二分搜索树。

3.3 二分搜索树的搜索操作
跟顺序表的二分搜索一样。废话不多说。直接贴代码

    int search(Node *node,int key){
        if(node!=NULL )
        {
            if(node->key==key){
                return node->value;
            }
            else if(node->key>key){
                return search(node->left,key);
            }
            else
            {
                return search(node->right,key);
            }
        }
        return -1;
    }

对应着在public 中定义一个search方法

    int search(int key){
        return search(root,key);
    }

是不是优雅高效?复制即可用。
3.3 二分搜索树的删除
这一步就麻烦了。
分几种情况。
a. 没有左孩子
二分搜索树的实现以及各种操作(支持重复节点)_第2张图片
遇到这种情况怎么办呢
其实很简单只需要用 这个节点的右孩子代替其位置即可。
于是有了

  if(node->left==NULL){
         Node *rightNode=node->right;  //拿到右孩子
         delete node;  //大胆删除节点
         count--;   // 数量--
         return rightNode; //将其返回
 }

b.没有右孩子
二分搜索树的实现以及各种操作(支持重复节点)_第3张图片
跟上面的思路一样

   if(node->right==NULL){
                Node *leftNode=node->left;  //拿到左孩子
                delete node;
                count--;
                return leftNode;
            }

c. 有左孩子也有右孩子
二分搜索树的实现以及各种操作(支持重复节点)_第4张图片
这个时候怎么办呢,其实只要找到右子树上最小的那个节点代替即可。
二分搜索树的实现以及各种操作(支持重复节点)_第5张图片
那么怎么去找这个最小的节点呢?很容易发现其实这个节点就是右子树最左边的那个节点。那么这个节点有什么特性呢?很明显这个节点是没有左孩子

    Node* minimun(Node *node){
        if(node->left==NULL){
            return node;   // 成功找到这个节点
        }
        return minimun(node->left);
    }

找到这个节点后就要想办法把它拿上来,并且被删除那个节点的左孩子和右孩子都要给他。

            Node *s=new Node(minimun(node->right)); //用s复制该节点的右子树的最小值的那个节点
            count++;
            s->right=removeMin(node->right);//把右子树那个最小值删掉并且做s的右子树
            s->left=node->left;
            delete node;
            count--;
            return s;

细心的同学会发现 这里使用了Node(Node *node)这样的构造函数,这是在类中没有的,所以我们要在类中定义一个重载的节点构造函数。(仅仅是复制而已!)


        Node(Node *node){
            this->value=node->value;
            this->key=node->key;
            this->right=node->right;
            this->left=node->left;
            this->frequency=node->frequency;
        }

好了不要晕,这里贴出完整代码

    Node *remove(Node *node,int key){
        if(node==NULL){
            return NULL;
        }
        if(node->key==key){    //找到这个节点
            if(node->left==NULL){// 情况a
                Node *rightNode=node->right;
                delete node;
                count--;
                return rightNode;
            }
            if(node->right==NULL){  //情况b
                Node *leftNode=node->left;
                delete node;
                count--;
                return leftNode;
            }
            // node->left!=NULL && node->right!=NULL  情况c
            Node *s=new Node(minimun(node->right)); //用s复制该节点的右子树的最小值的那个节点
            count++;
            s->right=removeMin(node->right);//把右子树那个最小值删掉并且做s的右子树
            s->left=node->left;
            delete node;
            count--;
            return s;
        }
        else if(node->key>key){  //左边找找看
            node->left=remove(node->left,key);
            return node;
        }
        else{  //右边找找看
            node->right=remove(node->right,key);
            return node;
        }
    }

相应在类中加入这个remove函数

   void remove(int key){
       root=remove(root,key);
    }  // 简单粗暴

这里基本操作讲完了,也就是插入,查找,删除。其时间复杂度都为O(logn)。
四、其他操作
这里贴出整个类的代码以及测试例子,供大家观赏。

#include <iostream>
#include <queue>
#include <ctime>
using namespace std;
//template 
class BST{
private:
    struct Node{
        int value;
        int key;
        int frequency;
        Node *left;
        Node *right;
        Node(int key ,int value){
            this->value=value;
            this->key=key;
            this->left=this->right=NULL;
            this->frequency=1;
        }//构造函数
        Node(Node *node){
            this->value=node->value;
            this->key=node->key;
            this->right=node->right;
            this->left=node->left;
            this->frequency=node->frequency;
        }
    };
    Node *root;
    int count;
public:
    BST(){
        root=NULL;
        count=0;
    }
    ~BST(){
        cout<<"已经销毁"<<endl;
        destroy(root);
    }
    int size(){
        return count;
    }
    bool isempty(){
        return count==0;
    }
    void insert(int key,int value){
        root=insert(root,key,value);
    }
    void preOrder(){
        preOrder(root);
    }
    void inOrder(){
        inOrder(root);
    }
    void postOrder(){
        postOrder(root);
    }
    int search(int key){
        return search(root,key);
    }
    void leverOrder(){
        levelOrder(root);
    }
    bool contain(int key){
        return contain(root,key);
    }
    int getFrequency(int key){
        return getFrequency(root,key);
    }
    int getMin(){
        return getMin(root);
    }
    int getMax(){
        return getMax(root);
    }
    void removeMin(){
        if(root!=NULL){
            root=removeMin(root);
        }
    }
    void removeMax(){
        if(root!=NULL){
            root=removeMax(root);
        }
    }
    void remove(int key){
       root=remove(root,key);
    }

private:
    Node *insert(Node *node,int key,int value){
        if(node==NULL){
            count++;
            return new Node(key,value);            
        }
        if(node->key< key){
            node->right=insert(node->right,key,value);
        }
        else if(node->key>key){
            node->left=insert(node->left,key,value);
        }
        else if(node->key==key)
        {
            node->value=value;
            node->frequency++;
        }
        return node;
    }
    void preOrder(Node *node){
        if(node!=NULL){
            cout<<node->key<<endl;
            preOrder(node->left);
            preOrder(node->right);
        }
    }
    void inOrder(Node *node){
        if(node!=NULL){
            inOrder(node->left);
            cout<<node->key<<endl;
            inOrder(node->right);
        }
    }
    void postOrder(Node *node){
        if(node!=NULL){
            postOrder(node->left);
            postOrder(node->right);
            cout<<node->key<<endl;
        }
    }
    void levelOrder(Node *node){
        queue<Node *>que;
        que.push(node);
        while (!que.empty()){
            Node *p=que.front();
            que.pop();
            cout<<p->key<<endl;
            if(p->left!=NULL){
                que.push(p->left);
            }
            if(p->right!=NULL){
                que.push(p->right);
            }
        }
    }
    int search(Node *node,int key){
        if(node!=NULL )
        {
            if(node->key==key){
                return node->value;
            }
            else if(node->key>key){
                return search(node->left,key);
            }
            else
            {
                return search(node->right,key);
            }
        }
        return -1;
    }
    int getFrequency(Node *node,int key){
        if(node!=NULL )
        {
            if(node->key==key){
                return node->frequency;
            }
            else if(node->key>key){
                return getFrequency(node->left,key);
            }
            else
            {
                return getFrequency(node->right,key);
            }
        }
        return -1;
    }
    bool contain(Node *node,int key){
        if(node!=NULL)
        {
            if(node->key==key){
                return true;
            }
            else if(node->key>key){
                return contain(node->left,key);
            } else{
                return contain(node->right,key);
            }
        }
        return false;

    }

    void destroy(Node *node){
        if(node!=NULL){
            destroy(node->left);
            destroy(node->right);
            delete node;
            count--;
        }
    }
    Node* minimun(Node *node){
        if(node->left==NULL){
            return node;
        }
        return minimun(node->left);
    }
    Node* maxmun(Node *node){
        if(node->right==NULL){
            return node;
        }
        return maxmun(node->right);
    }
    int getMin(Node *node){
        if(node->left==NULL){
            return node->key;
        }
        return getMin(node->left);
    }
    int getMax(Node *node){
        if(node->right==NULL){
            return node->key;
        }
        return getMax(node->right);
    }
    Node *removeMin(Node *node){
        if(node->left==NULL){
            Node *rightNode=node->right;
            delete node;
            count--;
            return rightNode;
        }
        else
        {
            node->left=removeMin(node->left);
            return node;
        }
    }
    Node *removeMax(Node *node){
        if(node->right==NULL){
            Node *leftNode=node->left;
            delete node;
            count--;
            return leftNode;
        }
        else
        {
            node->right=removeMax(node->right);
            return node;
        }
    }
    Node *remove(Node *node,int key){
        if(node==NULL){
            return NULL;
        }
        if(node->key==key){
            if(node->left==NULL){
                Node *rightNode=node->right;
                delete node;
                count--;
                return rightNode;
            }
            if(node->right==NULL){
                Node *leftNode=node->left;
                delete node;
                count--;
                return leftNode;
            }
            // node->left!=NULL && node->right!=NULL
            Node *s=new Node(minimun(node->right)); //用s复制该节点的右子树的最小值的那个节点
            count++;
            s->right=removeMin(node->right);//把右子树那个最小值删掉并且做s的右子树
            s->left=node->left;
            delete node;
            count--;
            return s;
        }
        else if(node->key>key){
            node->left=remove(node->left,key);
            return node;
        }
        else{
            node->right=remove(node->right,key);
            return node;
        }
    }
};
int main() {
    BST bst;
    srand(time(NULL));
    int a[7]={5,3,3,3,7,6,8};
    int n=10;
    for(int i=0;i<7;i++){
//        int key=rand()%n;
        int key=a[i];
        int value=2*key;
        bst.insert(key,value);
        cout<

上面所有操作都封装很好,可供大家二次开发,如果各位觉得有用复制之后点个赞哦!!!

你可能感兴趣的:(算法)