zoukankan      html  css  js  c++  java
  • 【数据结构】左偏树

    验证链接:左偏树:https://www.luogu.com.cn/problem/P3377
    验证链接:堆:https://www.luogu.com.cn/problem/P3378

    维护一种数据结构,支持下列操作:

    1. 插入新元素到某个集合中
    2. 查询某个集合中的最小元素
    3. 删除某个集合中的最小元素
    4. 合并两个集合

    在这个问题中,操作1~操作3是堆的正常操作,这里多了一个操作3,所以要实现的是一个可并堆。

    对上面这个问题进行明确:

    维护一种数据结构,支持下列操作:

    1. 插入新元素val到某个集合S_i中
    2. 查询某个集合S_i中的最小元素
    3. 删除某个集合S_i中的最小元素
    4. 合并两个集合S_i和S_j,合并后S_i和S_j都指向新的集合

    发现这个实际上需要结合并查集去做,利用并查集快速找到每个集合编号当前对应的新集合。新元素直接单独变成一个新集合,操作1归约到操作4。

    struct LeftistTree {
    
        static const int MAXN = 2e5 + 10;
    
        int lch[MAXN], rch[MAXN];
        int val[MAXN], dis[MAXN];
        int fnd[MAXN], del[MAXN];
    
        int _Find(int x) {
            return (fnd[x] == x) ? x : fnd[x] = _Find(fnd[x]);
        }
    
        int _Merge(int x, int y) {
            if(x == 0 || y == 0)
                return x + y;
            if(val[x] > val[y] || val[x] == val[y] && x > y)
                swap(x, y);
            rch[x] = _Merge(rch[x], y);
            if(dis[lch[x]] < dis[rch[x]])
                swap(lch[x], rch[x]);
            fnd[x] = x, fnd[lch[x]] = x, fnd[rch[x]] = x;
            dis[x] = dis[rch[x]] + 1;
            return x;
        }
    
        void Init(int *a, int n) {
            for(int i = 1; i <= n; ++i) {
                lch[i] = 0, rch[i] = 0;
                val[i] = a[i], dis[i] = 0;
                fnd[i] = i, del[i] = 0;
            }
        }
    
        void Merge(int x, int y) {
            if(del[x] == 1 || del[y] == 1)
                return;
            x = _Find(x), y = _Find(y);
            if(x == y)
                return;
            _Merge(x, y);
        }
    
        int Top(int x) {
            if(del[x] == 1)
                return -1;
            x = _Find(x);
            return val[x];
        }
    
        void Pop(int x) {
            if(del[x] == 1)
                return;
            x = _Find(x);
            del[x] = 1;
            int r = _Merge(lch[x], rch[x]);
            fnd[x] = r, fnd[lch[x]] = r, fnd[rch[x]] = r;
        }
    
    } lt;
    

    这个在Find的时候进行路径压缩,在Pop之后的x所在的堆的原本的根指向其两个孩子合并后的新根。
    _Merge的过程会重建被_Merge修改过的树的树状结构,但是_Merge总是只修改右侧的子树,所以路径压缩还是有点用的。

    Init() 使用数组a初始化左偏树森林,每个元素单独是一个堆。
    Merge(x,y) 合并第x个元素所属的左偏树和第y个元素所属的左偏树。若第x个元素或第y个元素已经被删除,直接返回。若第x个元素或第y个元素本身所属同一棵左偏树,直接返回。
    Top(x) 返回第x个元素所属的左偏树的堆顶。若第x个元素已经被删除,返回-1。
    Pop(x) 删除第x个元素所属的左偏树的堆顶。若第x个元素已经被删除,直接返回。

    这里的堆顶,因为是if(val[x] > val[y] || val[x] == val[y] && x > y) swap(x, y); ,所以是小根堆,当多个元素的值都是最小值时,把序号小的放在堆顶。

    要作为普通的堆使用时,给每个堆初始化放一个INF元素防止这个堆变成空堆就行(记得删除del相关的字段)。

  • 相关阅读:
    1.在虚拟机中安装Linux中的CentOS7系统
    Mysql的跨服务器 关联查询--Federated引擎
    mysql 时间格式转换
    mysql 查询当天、本周,本月,上一个月的数据
    mybatis执行批量更新数据
    JSTL 递增序号
    mybaits 新增数据返回id
    第一部分软件测试综述——软件测试背景【软件测试】(美)Ron Patton中文电子版
    测试真的是一个无聊又没前途的岗位吗?是吗?不是吗?
    碎片化时间,偷偷变牛逼!2020全栈软件测试工程师修炼手册
  • 原文地址:https://www.cnblogs.com/purinliang/p/14314630.html
Copyright © 2011-2022 走看看