zoukankan      html  css  js  c++  java
  • 二叉搜索树BST

      二叉搜索树(Binary Search Tree)它要么是一棵空树,要么是一棵具有下列性质的二叉树:若它的左子树不空,则左子树上所有结点的值均小于它的根结点的值; 若它的右子树不空,则右子树上所有结点的值均大于它的根结点的值; 它的左,右子树也分别为二叉搜索树

      二叉搜索树上的每一个结点都维护四个值,一个是它本身的权值data,有siz表示当前点为根的子树大小,ls记录左儿子的编号,rs记录右儿子的编号

    1. 插入节点(insert)

    插入节点的时候从根节点出发

    (1) 如果当前节点为空,则将data赋为我们要插入的节点,siz初始化为1,再将其父亲连向他(这里我们要注意,二叉搜索树,是不记录父节点的,为了能使其父亲连向他,我们每次调用insert函数的时候我们传入其父亲指向其子节点的地址,而我们要使其父亲指向它,就是将该地址上的值改为新节点的编号)

    (2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

    (3) 如果要插入的节点比我们当前的节点大等于我们当前的节点,就向该节点的右子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

    是不是很容易呀,实现如下:

    void insert(int &now,int v)
    {
        if(!now)
        {
            data[++tot]=v;
            siz[tot]=1;
            now=tot;
            return;
        }
        ++siz[now];
        if(data[now]<=v) insert(rs[now],v);
        else insert(ls[now],v);
    }

    2. 删除节点(del)

    其实删除操作非常奇怪,我们不可以直接删除一个有两个子节点的节点,我们从根节点出发

    (1) 如果当前节点为空就返回

    (2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

    (3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树前进,同时,将该节点的siz加1(因为要插入的节点一定是插入到该节点的子树中去了)

    (4) 如果要插入的节点等于我们当前的节点

    a) 如果该点只有一个儿子,就直接将该点的父亲连向该点的儿子,连接方法如上

    b) 不然,我们就找到比该点大的最小点,将两点交换,再删除比该点大的最小点

    是不是很容易呀,实现如下:

    void del(int &now,int v)
    {
        if(!now) return;
        --siz[now];
        if(data[now]==v)
        {
            if(!ls[now]||!rs[now]) now=ls[now]+rs[now];
            else
            {
                int temp=rs[now];
                while(ls[temp]) temp=ls[temp];
                data[now]=data[temp];
                del(rs[now],data[temp]);
            }
        }
        else if(data[now]<=v) del(rs[now],v);
        else del(ls[now],v);
    }

     3. 查询某数排名(rank)

    我们从根节点出发

      (1) 如果当前节点为空就返回1

    (2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询

    (3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询,同时将查询得到的值+siz+1后返回

      其实很容易写错,实现如下:

    int rank(int &now,int v)
    {
        if(!now) return 1;
        if(data[now]<v) return siz[ls[now]]+1+rank(rs[now],v);
        else return rank(ls[now],v);
    }

    4. 查询第k大的数(kth)

    我们从根节点出发

    (1) 如果当前节点为空就返回-1(找不到)

    (2) 如果要查的排名等于siz[ls[now]]+1,就返回该节点

    (3) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询

    (4) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询排名为k-siz-1的数

    其实很容易写错,实现如下:

    int kth(int &now,int v)
    {
        if(!now) return -1;
        if(siz[ls[now]]+1==v) return data[now];
        else if(siz[ls[now]]<v) return kth(rs[now],v-1-siz[ls[now]]);
        else return kth(ls[now],v);
    }

    5. 求某数的前驱(前驱定义为小于该数,且最大的数)(pred)

    我们从根节点出发

    (1) 如果当前节点为空就返回-0x3f3f3f3f

    (2) 如果要插入的节点比我们当前的节点小或等于我们当前的节点,就向该节点的左子树继续查询

    (3) 如果要插入的节点比我们当前的节点大,就向该节点的右子树继续查询,并将查询得到的值与该点取最大值返回

      其实很容易写错,实现如下

    int pred(int &now,int v)
    {
        if(!now) return -0x3f3f3f3f;
        if(data[now]<v) return max(data[now],pred(rs[now],v));
        else return pred(ls[now],v);
    }

    6. 求某数的后继(后继定义为大于该数,且最小的数)(succ)

    我们从根节点出发

    (1) 如果当前节点为空就返回0x3f3f3f3f

    (2) 如果要插入的节点比我们当前的节点小,就向该节点的左子树继续查询,并将查询得到的值与该点取最小值返回

    (3) 如果要插入的节点比我们当前的节点大或等于我们当前的节点,就向该节点的右子树继续查询

      其实很容易写错,实现如下:

    int succ(int &now,int v)
    {
        if(!now) return 0x3f3f3f3f;
        if(data[now]<=v) return succ(rs[now],v);
        else return min(data[now],succ(ls[now],v));
    }

    是不是很容易呀,完整代码如下:

    #include <iostream>
    #include <cstdio>
    #include <cstring>
    #include <cmath>
    #include <map>
    #include <cstdlib>
    #include <algorithm>
    #include <queue>
    #include <stack>
    using namespace std;
    inline int read()
    {
        int a=0,q=0;
        char ch=getchar();
        while((ch<'0'||ch>'9')&&ch!='-') ch=getchar();
        if(ch=='-')  q=1,ch=getchar();
        while(ch>='0'&&ch<='9') a=(a<<3)+(a<<1)+ch-48,ch=getchar();
        return q?-a:a;
    }
    const int N=100100;
    int tot(0),root(0),siz[N],ls[N],rs[N],data[N],op,x,n;
    void insert(int &now,int v)
    {
        if(!now)
        {
            data[++tot]=v;
            siz[tot]=1;
            now=tot;
            return;
        }
        ++siz[now];
        if(data[now]<=v) insert(rs[now],v);
        else insert(ls[now],v);
    }
    void del(int &now,int v)
    {
        if(!now) return;
        --siz[now];
        if(data[now]==v)
        {
            if(!ls[now]||!rs[now]) now=ls[now]+rs[now];
            else
            {
                int temp=rs[now];
                while(ls[temp]) temp=ls[temp];
                data[now]=data[temp];
                del(rs[now],data[temp]);
            }
        }
        else if(data[now]<=v) del(rs[now],v);
        else del(ls[now],v);
    }
    int rank(int &now,int v)
    {
        if(!now) return 1;
        if(data[now]<v) return siz[ls[now]]+1+rank(rs[now],v);
        else return rank(ls[now],v);
    }
    int kth(int &now,int v)
    {
        if(!now) return -1;
        if(siz[ls[now]]+1==v) return data[now];
        else if(siz[ls[now]]<v) return kth(rs[now],v-1-siz[ls[now]]);
        else return kth(ls[now],v);
    }
    int pred(int &now,int v)
    {
        if(!now) return -0x3f3f3f3f;
        if(data[now]<v) return max(data[now],pred(rs[now],v));
        else return pred(ls[now],v);
    }
    int succ(int &now,int v)
    {
        if(!now) return 0x3f3f3f3f;
        if(data[now]<=v) return succ(rs[now],v);
        else return min(data[now],succ(ls[now],v));
    }
    int main()
    {
        // freopen("data","r",stdin);
        // freopen("output","w",stdout);
        n=read();
        while(n--)
        {
            scanf("%d%d",&op,&x);
            if(op==1) insert(root,x);
            else if(op==2) del(root,x);
            else if(op==3) printf("%d
    ",rank(root,x));
            else if(op==4) printf("%d
    ",kth(root,x));
            else if(op==5) printf("%d
    ",pred(root,x));
            else printf("%d
    ",succ(root,x));
        }
        return 0;
    }
    /*
    
    */
  • 相关阅读:
    递增一个指针
    ubuntu 系统 sudo apt-get update遇到问题sub-process returned an error code
    熟悉HDFS过程中遇到的问题
    大二暑假第八周进度报告
    大二暑假第七周进度报告
    oracle“ORA-00904”错误:标识符无效
    大学暑假第六周进度报告
    大二暑假第五周进度报告
    使用Navicat for Oracle新建表空间、用户及权限赋予
    大学暑假第四周进度报告
  • 原文地址:https://www.cnblogs.com/cold-cold/p/9977785.html
Copyright © 2011-2022 走看看