• 二叉搜索树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;
    }
    /*
    
    */
  • 相关阅读:
    Hia~hia~
    细节与销售
    敛财术
    酒井法子
    买了新手机NOKIA E71
    观《拉贝日记》
    居然不配套
    2009上海最新“四金”及个人所得税计算(器)
    有好听点的新歌么?
    我家乌龟终于下蛋了!
  • 原文地址:https://www.cnblogs.com/cold-cold/p/9977785.html
Copyright © 2020-2023  润新知