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

    前置芝士:堆

    可并堆

    可并堆就是满足堆性质且可以在$O(log{n})$的复杂度内合并两个堆的数据结构。常见的有斜堆,左偏树等。

    这篇文章就来介绍一下左偏树。

    首先有个问题:直接用二叉堆不香吗?

    答案是肯定的。因为二叉堆合并的时间复杂度是$O(n)$的。

    所以我们有了左偏树这个数据结构。

    左偏树

    一些记号与定义

    val:每个节点的权值,堆按这个排序。

    外节点:没有左孩子或右孩子或都没有的节点。

    dist:空节点的dist是0,外节点的dist是1,其余节点的dist为到最近的外节点的距离。

    一棵左偏树的dist:记堆顶的dist为这课左偏树的dist。

    性质

    左偏树顾名思义就是朝左偏的树。它在满足堆性质的基础上还要满足左孩子的dist大于等于右孩子的dist(所以左偏嘛)。

    在合并时全部怼到右孩子去就可以保证在log的时间复杂度内合并了。

    显然,对于一个非外节点的dist等于其右孩子的dist+1。

    基本操作

    合并

    合并时左偏树最重要的一步,我们以小根堆为例。

    假设我们要合并两个堆的堆顶分别是u,v。

    于是我们为了满足小根堆的性质找出val较小的点做为新左偏树的顶点,这里不妨设其为u。

    于是基于启发式合并的思想,我们把u的右孩子和v合并,递归去做就可以了。

    因为一棵左偏树的dist最多是$ceil(log{n})$,每次合并必有一棵左偏树的dist减1,所以这样合并的复杂度是$O(log{n}+log{m})$。

    合并完有可能打破左偏树的性质,要更新。

    int Merge(int u, int v) {
        if (!u || !v) return u | v;
        if (val[u] > val[v]) swap(u, v);
        rson[u] = merge(rson[u], v);
        if (dist[lson[u]] < dist[rson[u]]) swap(lson[u], rson[u]);
        dist[u] = dist[rson[u]] + 1;
        return u;
    }

    查找最小(最大值)

    直接返回堆顶即可。

    删除最小(最大值)

    合并堆顶的两个孩子即可

    删除任意给定节点

    注意这里指的是给定节点,就是说告诉你了是几号节点。左偏树是不能快速删除某个权值的所有节点的。

    这东西没什么鸟用,反正我至今没有遇到这样的题,就不讲了。其实是我太弱了。

    思想和删除堆顶类似。

    建树

    像并查集一样,每个点就是一棵左偏树,然后再合并即可。

    插入新节点同理直接合并即可。

    给整个堆加上或乘上某个数

    类似于线段树懒标记的思想,给堆顶打个标记,合并的时候再通过pushdown下放下去。

    后面有一道例题。

    例题

    罗马游戏(模板题)

    Monkey King(维护大根堆)

    [APIO2012]派遣(树上问题)

    「JLOI2015」城池攻占(树上问题+打标记)

     

  • 相关阅读:
    Hibernate unsaved-value 属性
    ResulsetHandler九个实现类
    Introspector内省和反射的区别.
    数据库表、字段命名规范
    Linux 时间同步 ntpdate
    http升级https改造方案
    org.apache.tomcat.util.net.NioEndpoint,打开的文件过多
    kafka常用命令
    elasticsearch.yml 配置说明
    getDate() 各种时间格式
  • 原文地址:https://www.cnblogs.com/zcr-blog/p/12759955.html
Copyright © 2011-2022 走看看