zoukankan      html  css  js  c++  java
  • Treap

    Treap

    普通二叉搜索树(BST)

    • 对于任一棵子树,根节点权值大于左子树所有结点的权值,小于右子树所有结点权值

    1. 结点结构体&初始化函数

    struct BST{
        int l,r;
        int val;
    }a[SIZE];
    int tot,root,INF = 1<<30;
    int New(int val){
        a[++tot].val = val;return tot;
    }
    void Build(){
        New(-INF),New(INF);//加两个无穷结点可以省很多事
        root = -1,a[1].r = 2;
    }
    

    2. 查询操作

    • 如果递归到当前结点为空结点,则说明找不到
    • 如果递归结点权值等于val,则返回当前结点
    • 否则与查询对象val比大小,决定去左还是去右边
    int Get(int p,int val){
        if(p == 0)return 0;
        if(val == a[p].val)return p;
        return val < a[p].val ? Get(a[p].l,val) : Get(a[p].r,val);
    }
    

    3. 插入操作

    • 当前所指结点为空结点,则new一个新的,返回新结点编号
    • 如果已经存在权值为val的结点,则不新建
    • 当前结点权值与val比大小,决定递归左边还是右边,注意这里函数(Insert)函数的孩子“指针”参数为引用,可以实际修改。
    void Insert(int &p,int val){
        if(!p){
            p = New(val);
            return ;
        }
        if(val == a[p].val)return;
        if(val < a[p].val)Insert(a[p].l,val);
        else Insert(a[p].r,val);
    }
    

    4. 寻找后继结点,即权值大于val的前提下,关键码最小的结点

    • 如果存在一个权值为val的结点
      • 如果它有右子树,那么该子树最左边的结点就是val的后继结点
      • 如果没有,那么从root到该节点的路径上选一个权值符合的就是val的后继节点
    • 根据当前结点权值与val作比较决定递归左子树或右子树,同时用当前结点更新答案(也就是看看这个点能否更新当前答案保存的后继结点)
    int GetNext(int val){
        int ans = 2;
        int p = root;
        while(p){
            if(val == a[p].val){//找到了
                if(a[p].r > 0){//存在右子树
                    p = a[p].r;
                    while(a[p].l)p = a[p].l;//找右子树的最左边
                    ans = p;
                }
                break;
            }
            if(a[p].val > val && a[p].val < a[ans].val)ans = p;//用路径上的点来更新
            p = val < a[p].val ? a[p].l : a[p].r;//决定递归结点
        }
        return p;
    }
    

    5. 删除结点

    • 如果当前结点为空结点,返回
    • 如果找到了
      • 如果只有一颗子树,那么把子树跟它的父亲连起来
      • 如果有两个子树,找到它的后继结点(也就是右子树的最左侧的结点),然后因为后继结点一定没有左子树,所以可以直接删除,然后将这个后继结点替换当前要删除的结点即可。
    • 如果没找到就继续递归找
    void Remove(int &p,int val){
        if(p == 0)return;
        if(val == a[p].val){
            if(a[p].l == 0)p = a[p].r;
            else if(a[p].r == 0)p = a[p].l;
            else{
                int nxt = a[p].r;
                while(a[nxt].l)nxt = a[nxt].l;
                Remove(a[p].r,a[nxt].val);
                a[nxt].l = a[p].l,a[nxt].r = a[p].r;
                p = nxt;
            }
            return;
        }
        if(val < a[p].val)Remove(a[p].l,val);
        else Remove(a[p].r,val);
    }
    

    Treap

    由于升序或降序序列依次插入到BST中会导致二叉查找树查找效率退化,所以要在某些时刻进行适当调整。如果在new一个新结点的时候,给它随机赋值一个值,在满足BST的前提下,按照这个值去维护一个大顶堆((Tree+heap = Treap))。那么期望下他的高度是相对较平衡的(感性理解,就是叶子结点的高度相差不多)

    另外模板题 Luogo 3369 中每个相同权值的数有多个,加上还需要求排名等信息,所以Treap结点结构体如下

    struct Treap{
        int l,r;
        int val,dat;
        int cnt,size;
    }a[N];
    int tot,root,n,INF = 0x7fffffff;
    int New(int val){
        a[++tot].val = val;
        a[tot].dat = rand();//给随机权值赋值
        a[tot].cnt = a[tot].size = 1;return tot;
    }
    //更新当前结点所代表子树的size
    void Update(int p){
        a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt;
    }
    void Build(){
        New(-INF);New(INF);
        root = 1;a[1].r = 2;
        Update(1);
    }
    

    1. 查询数值x的排名

    • 如果当前节点权值等于x,则返回该节点左子树size+1
    • 如果当前节点权值大于x,那么返回左子树查询结果
    • 否则返回右子树查询结果+左子树size+当前结点cnt(cnt为当前结点权值的个数)
    int GetRankByVal(int p,int val){
        if(p == 0)return 0;
        if(val == a[p].val)return a[a[p].l].size + 1;//排名,+1
        if(val < a[p].val)return GetRankByVal(a[p].l,val);
        return GetRankByVal(a[p].r,val) + a[a[p].l].size + a[p].cnt;
    }
    

    2. 根据排名查询值

    • 如果当前节点不存在,返回INF
    • 如果当前节点左子树size大于等于rank,那么答案在左子树中
    • 如果左子树size + 当前节点cnt 大于等于rank,那么答案就是当前节点的权值
    • 否则答案在右子树中,减去左子树和当前节点的影响继续递归右子树即可
    int GetValByRank(int p,int rank){
        if(p == 0)return INF;
        if(a[a[p].l].size >= rank)return GetValByRank(a[p].l,rank);
        if(a[a[p].l].size + a[p].cnt >= rank)return a[p].val;
        return GetValByRank(a[p].r,rank - a[a[p].l].size - a[p].cnt);
    }
    

    3. 右旋

    void zig(int &p){
        int q = a[p].l;
        a[p].l = a[q].r;a[q].r = p;p = q;
        Update(a[p].r),Update(p);
    }
    

    4. 左旋

    void zag(int &p){
        int q = a[p].r;
        a[p].r = a[q].l;a[q].l=p;p = q;
        Update(a[p].l),Update(p);
    }
    

    5. 插入

    • 与BST大致相同,只是在把val插入到左子树或右子树时,要根据dat来维护Treap,即通过左旋或右旋调整使得堆性质成立
    • 最后要Update
    void Insert(int &p,int val){
        if(p == 0){
            p = New(val);return;
        }
        if(val == a[p].val){
            a[p].cnt ++;Update(p);
            return;
        }
        if(val < a[p].val){
            Insert(a[p].l,val);
            if(a[p].dat < a[a[p].l].dat)zig(p);
        }
        else{
            Insert(a[p].r,val);
            if(a[p].dat < a[a[p].r].dat)zag(p);
        }
        Update(p);
    }
    

    6. 获取前继节点和后继节点

    int GetPre(int val){
        int ans = 1;
        int p = root;
        while(p){
            if(val == a[p].val){
                if(a[p].l){
                    p = a[p].l;
                    while(a[p].r) p = a[p].r;
                    ans = p;
                }
                break;
            }
            if(a[p].val < val && a[p].val > a[ans].val)ans = p;
            p = val < a[p].val ? a[p].l : a[p].r;
        }
        return a[ans].val;
    }
    int GetNext(int val){
        int ans = 2;
        int p = root;
        while(p){
            if(val == a[p].val){
                if(a[p].r>0){
                    p = a[p].r;
                    while(a[p].l)p = a[p].l;
                    ans = p;
                }
                break;
            }
            if(a[p].val > val && a[p].val < a[ans].val)ans = p;
            p = val < a[p].val ? a[p].l : a[p].r;
        }
        return a[ans].val;
    }
    

    7. 删除结点

    • 因为Treap可以左旋或右旋,所以可以把要删除的结点进行左旋或右旋(旋转的时候要保证堆性质成立)到叶子节点进行删除
    void Remove(int &p,int val){
        if(p == 0)return;
        if(val == a[p].val){
            if(a[p].cnt > 1){
                a[p].cnt --;
                Update(p);
                return;
            }
            if(a[p].l || a[p].r){//不是叶子结点,向下旋转
                if(a[p].r == 0 || a[a[p].l].dat > a[a[p].r].dat){
                    zig(p),Remove(a[p].r,val);
                }
                else {
                    zag(p);Remove(a[p].l,val);
                }
                Update(p);
            }
            else p = 0;//叶子节点,删除
    
            return;
        }
        val < a[p].val ? Remove(a[p].l,val) : Remove(a[p].r,val);
        Update(p);
    }
    

    附Luogu3369
    Luogu3369

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 100100;
    struct Treap{
        int l,r;
        int val,dat;
        int cnt,size;
    }a[N];
    int tot,root,n,INF = 0x7fffffff;
    int New(int val){
        a[++tot].val = val;
        a[tot].dat = rand();
        a[tot].cnt = a[tot].size = 1;return tot;
    }
    void Update(int p){
        a[p].size = a[a[p].l].size + a[a[p].r].size + a[p].cnt;
    }
    void Build(){
        New(-INF);New(INF);
        root = 1;a[1].r = 2;
        Update(1);
    }
    int GetRankByVal(int p,int val){
        if(p == 0)return 0;
        if(val == a[p].val)return a[a[p].l].size + 1;//排名,+1
        if(val < a[p].val)return GetRankByVal(a[p].l,val);
        return GetRankByVal(a[p].r,val) + a[a[p].l].size + a[p].cnt;
    }
    int GetValByRank(int p,int rank){
        if(p == 0)return INF;
        if(a[a[p].l].size >= rank)return GetValByRank(a[p].l,rank);
        if(a[a[p].l].size + a[p].cnt >= rank)return a[p].val;
        return GetValByRank(a[p].r,rank - a[a[p].l].size - a[p].cnt);
    }
    void zig(int &p){
        int q = a[p].l;
        a[p].l = a[q].r;a[q].r = p;p = q;
        Update(a[p].r),Update(p);
    }
    void zag(int &p){
        int q = a[p].r;
        a[p].r = a[q].l;a[q].l=p;p = q;
        Update(a[p].l),Update(p);
    }
    void Insert(int &p,int val){
        if(p == 0){
            p = New(val);return;
        }
        if(val == a[p].val){
            a[p].cnt ++;Update(p);
            return;
        }
        if(val < a[p].val){
            Insert(a[p].l,val);
            if(a[p].dat < a[a[p].l].dat)zig(p);
        }
        else{
            Insert(a[p].r,val);
            if(a[p].dat < a[a[p].r].dat)zag(p);
        }
        Update(p);
    }
    int GetPre(int val){
        int ans = 1;
        int p = root;
        while(p){
            if(val == a[p].val){
                if(a[p].l){
                    p = a[p].l;
                    while(a[p].r) p = a[p].r;
                    ans = p;
                }
                break;
            }
            if(a[p].val < val && a[p].val > a[ans].val)ans = p;
            p = val < a[p].val ? a[p].l : a[p].r;
        }
        return a[ans].val;
    }
    int GetNext(int val){
        int ans = 2;
        int p = root;
        while(p){
            if(val == a[p].val){
                if(a[p].r>0){
                    p = a[p].r;
                    while(a[p].l)p = a[p].l;
                    ans = p;
                }
                break;
            }
            if(a[p].val > val && a[p].val < a[ans].val)ans = p;
            p = val < a[p].val ? a[p].l : a[p].r;
        }
        return a[ans].val;
    }
    void Remove(int &p,int val){
        if(p == 0)return;
        if(val == a[p].val){
            if(a[p].cnt > 1){
                a[p].cnt --;
                Update(p);
                return;
            }
            if(a[p].l || a[p].r){//不是叶子结点,向下旋转
                if(a[p].r == 0 || a[a[p].l].dat > a[a[p].r].dat){
                    zig(p),Remove(a[p].r,val);
                }
                else {
                    zag(p);Remove(a[p].l,val);
                }
                Update(p);
            }
            else p = 0;//叶子节点,删除
            return;
        }
        val < a[p].val ? Remove(a[p].l,val) : Remove(a[p].r,val);
        Update(p);
    }
    int main(){
        Build();
        scanf("%d",&n);
        while(n--){
            int opt,x;
            scanf("%d%d",&opt,&x);
            switch(opt){
                case 1:
                    Insert(root,x);
                    break;
                case 2:
                    Remove(root,x);
                    break;
                case 3:
                    printf("%d
    ",GetRankByVal(root,x)-1);
                    break;
                case 4:
                    printf("%d
    ",GetValByRank(root,x+1));
                    break;
                case 5:
                    printf("%d
    ",GetPre(x));break;
                case 6:
                    printf("%d
    ",GetNext(x));break;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    机器学习规则:ML工程最佳实践----rules_of_ml section 2【翻译】
    机器学习规则:ML工程最佳实践----rule_of_ml section 3【翻译】
    知识图谱技术分享会----有关知识图谱构建的部分关键技术简介及思考
    【e2Open】
    【2B】企业供应链管理软件
    【交互】复杂逻辑配置的一个不错的方法(神策数据)
    【视觉】交易数据展示
    【视觉】数据平台
    【设计复盘】APP设计复盘
    【设计规范】腾讯课堂
  • 原文地址:https://www.cnblogs.com/1625--H/p/11323626.html
Copyright © 2011-2022 走看看