zoukankan      html  css  js  c++  java
  • 学习笔记:左偏树

    左偏树是一种可并堆,除了堆的基本功能,最大的特点就是支持合并堆,甚至比普通堆好写。

    下面叙述以小根堆为例,大根堆对称。

    支持的功能:

    1. (O(log n)) 求一个数所在堆的根
    2. (O(1)) 求最小值
    3. (O(log n)) 合并两个堆
    4. (O(log n)) 删除最小值
    5. (O(log n)) 插入一个数

    (n) 是插入的总节点数(或当前堆的节点数)。

    维护的信息:

    struct T{
        int l, r, v, d, f;
        // l, r 表示左右儿子, v 表示值
        // d 表示从当前节点到其子树中最近叶子节点的距离 + 1, f 表示当前节点的父亲
    } t[N];
    

    基本的结构还是堆,即对于任意节点,它的权值小于等于其子树中任意权值,因此查询最小值只需 (O(1)) 访问根即可。

    左偏的意义就是:对于任意一个节点的左儿子 (ls) 和右儿子 (rs),都有 (t[ls].d ge t[rs].d),感性理解就是左子树深的更长。

    性质:对于一棵根节点 (rt) 满足 (t[rt].d = k) 的堆而言,至少有 (2^k - 1) 个节点,即一个高度为 (k) 的满二叉树的节点树,因为这些节点必不可少,否则 (d) 就小于 (k) 了。因此对于一个有 (n) 个节点的堆,根节点的 (d) 就是 (log n) 级别的。

    操作:

    求一个数所在堆的根

    朴素上我们可以一个个跳 (t[x].f)。不过我们可以把 (t[x].f) 看做一个并查集 (fa) 数组,路径压缩一下:

    int find(int x) {
        return t[x].f == x ? x : t[x].f = find(t[x].f);
    }
    

    这样只要保证我们之后的赋值 (fa) 操作都是类似并查集的合并操作,那么复杂度就是 (O(log n)) 的。

    求最小值:

    找到一个数所在根,直接访问根节点值即可。

    合并两个堆

    合并 (merge(x, y)) 分别以 (x, y) 为根的两个小根堆,并返回合并完的根编号:不妨设 (t[x].v < t[y].v)(若不满足 ( ext{swap}) ) ,接着只需递归 (merge(t[x].r, y))。回溯时检查 (x) 左右儿子的 (d),若不满足左偏树关系交换,返回 (x) 即可。

    时间复杂度,每次递归,(x, y) 之一的 (d) 必然减少 (1),做多减少到 (0),而 (d)(log n) 量级的,所以复杂度是 (O(log n))

    貌似网上的复杂度都不是很对,不能每次都赋 (t[x].fa = x),这样复杂度就假了,而是函数调用之前把 (t[y].f = x),然后内部合并不改变 (f) 的值,这样相当于合并两个联通块,复杂度是对的。

    int merge(int x, int y) { // 递归合并函数
        if (!x || !y) return x + y;
        if (t[x].v > t[y].v || (t[x].v == t[y].v && x > y)) swap(x, y);
        rs = merge(rs, y);
        if (t[ls].d < t[rs].d) swap(ls, rs);
        t[x].d = t[rs].d + 1;
        return x;
    }
    
    int work(int x, int y) { // 合并 x, y 两个堆。
        if (t[x].v > t[y].v || (t[x].v == t[y].v && x > y)) swap(x, y);
        t[x].f = t[y].f = x;
        merge(x, y); return x;
    }
    

    删除最小值

    找到根节点后,合并两个子树。

    void del(int x) {
        t[x].f = work(ls, rs);
    }
    

    插入一个数

    直接单开一个节点,合并就好了。

  • 相关阅读:
    面试题
    网络编程-1
    excel文件导入数据库--jxl包
    excel文件导入数据库
    1113 Integer Set Partition (25 分)集合分割
    1120 Friend Numbers (20 分)set的使用
    1099 Build A Binary Search Tree (30 分)
    1092 To Buy or Not to Buy (字符串删除)
    1127 ZigZagging on a Tree (30 分)树的层次遍历
    1155 Heap Paths (30 分)判断是否是一个堆
  • 原文地址:https://www.cnblogs.com/dmoransky/p/13916075.html
Copyright © 2011-2022 走看看