首页 > 其他分享 >【博客】左偏树

【博客】左偏树

时间:2024-03-19 16:25:11浏览次数:21  
标签:dist int 合并 距离 博客 节点 左偏

左偏树

前言

左偏树是一棵向左偏的树

image


左偏树是一种能在\(O(\log n)\)之内完成合并的可并堆

长这样

image

我们常用左偏树完成以下操作

  1. 在指定集合中插入一个元素
  2. 查询集合中最高优先级的元素
  3. 删除集合中最高优先级的元素
  4. 删除指定元素
  5. 合并两个集合

性质

首先 我们要知道左偏树的每个节点都维护了什么东西

  1. 左右子节点 父亲节点 (父亲孩子表示法)
  2. 键值key
    (上文提到的优先级)其实就是我们要维护的数据(@某人说所有东西都是数据)

补一个定义

左子树或右子树为空的节点称为外节点,左子树和右子树可以同时为空。

  1. 距离dist
    不是外节点的节点 的距离为其到子树中最近的外节点的距离,外节点 的距离为0,空节点 的距离为-1

然后我们就可以说左偏树的一些性质

1.节点的键值小于等于左右子节点的键值

说白了就是 键值满足小顶堆的性质

这个性质保证了我们可以在O(1)的时间复杂度内查询最小值(就是堆顶呀)

2.结点的左子结点的距离不小于右子结点的距离

这就是左偏树为什么叫左偏树的原因

3.节点的距离等于它的右子节点的距离加1

注意距离的概念跟深度不同 左偏树并不一定是左边节点数深度大于右边的树


操作

合并

把两棵左偏树A,B合并成一棵左偏树C

1.最简单的情况就是一棵树为空 直接返回另一棵树即可

if(!A) return B;
if(!B) return A;

2.若不为空 假设A的根节点的键值小于B的根节点的键值(大于的话换一下不就好啦)那么把A的根节点作为C的根节点,递归合并A的右子树和B

if(key[B]<key[A]) swap(A,B); //B小于A的话就换一下
r[A]=Merge(r[A],B); //递归合并

3.合并之后 r[A]的距离可能变大 性质2可能被破坏 如果破坏了 需要交换一下A的左右子树

if(dist[r[A]]>dist[l[A]]) swap(l[A],r[A]); //不满足左偏性质 交换左右子树

4.r[A]的距离可能被改变,要更新A的距离

if(!r[A]) dist[A]=0;
else dist[A]=dist[r[A]]+1; //更新距离
return A; //合并好了 返回一下

合并的总代码为

int Merge(int A,int B)
{
    if(!A) return B;
    if(!B) return A;
    if(key[B]<key[A]) swap(A,B); //B小于A的话就换一下
    r[A]=Merge(r[A],B); //递归合并
    if(dist[r[A]]>dist[l[A]]) swap(l[A],r[A]); //不满足左偏性质 交换左右子树
    if(!r[A]) dist[A]=0;
    else dist[A]=dist[r[A]]+1; //更新距离
    return A; //合并好了 返回一下
}

单点插入

单个结点也可以看成一棵左偏树

所以可以看作合并操作

void Insert(int x,int A) //把x插入到A中
{
    int B=MakeIntoTree(x); //新建一棵只有根节点的左偏树
    A=Merge(A,B); //合并
}

删除根结点

将根结点的左右子树合并即可

int DeleteMin()
{
    int t=key[root];
    root=Marge(l[root],r[root]);//合并左右子树
    return t;
}

构建一棵左偏树

法1:将结点一个个单点插入(谁(sei)还那么做啊~)

梅姐附体

时间复杂度\(O(n\log n)\)

法2:

1.将结点放入到队列中

2.从队首取出两棵树 合并后插入队尾

3.当队列中只剩下一棵树时结束

时间复杂度\(O(n)\)

代码长介样

void Build()
{
    //此处节点队列为h,头尾为l、r
    int l=1,r=n;
    for(int i=1;i<n;i++)//可证只有n-1次合并
    {
        int root=Merge(h[l],h[l+1]);
        r++;
        h[r]=root;
        l+=2;
    }
}

删除任意已知结点

左偏树不能有效搜索指定键值的结点。

因为它只是堆 不是二叉搜索树

所以删除任意已知节点,首先要找到这个已知节点

需要遍历一遍,在O(n)的时间里找到这个节点。

找到这个结点后,设要删除的节点为x,合并x的左右子树然后自底向上维护距离值不满足左偏性质则交换左右儿子,直至距离值无需更新时,算法结束。

具体而言,设q是x的父节点。合并左右儿子后的新树设为p,即p=Merge(left(x),right(x)),设节点x的父节点为q,具体分析如下:

(1)x是原树中的根节点,则删除x节点后,合并左右子树后,算法结束

(2)x不是原树中的根节点,合并后的树为p,新的距离为dist(p),dist(p)与dist(q)的值相比有如下几种情况

(a) dist(p)=dist(q)-1 满足左偏条件,不需进行交换

(b)dist(p)<dist(q)-1

如果p是q的右子树,则需要更新q的距离值,并一直更新到距离值无需再更新时为止

如果p是q的左子树,则需要交换p和right(q),并更新q的距离值,并一直更新到距离值无需再更新时为止

期间不满足左偏条件的要进行交换

(c)dist(p)>dist(q)-1

如果新树p在q的左子树上,则满足左偏条件,不需要交换和更新。

如果新树p在q的右子树上,如果新树p的距离大于q的左子树的距离,则需要交换,同时更新其q的距离,并一直更新到距离值无需再更新时为止,期间不满足左偏条件的要进行交换。

void Delete(int x)
{
    int q=parent[x];
    int p=Marge(l[x],r[x]);
    parent[p]=q;
    if(q && l[q]==x) l[q]=p;
    if(q && r[q]==x) r[q]=p;
    while(q)
    {
        if(dist[l[q]]<dist[r[q]])
            swap(l[q],r[q]);
        if(dist[r[q]]+1==dist[q]) return ;
        dist[q]=dist[r[q]]+1;
        p=q;
        q=parent[p];
    }
}

左偏树的基本操作就完事啦

关键还是合并操作

完结撒花


附上一道模板题

P3377【模板】左偏树/可并堆 - 洛谷

再附上两道题

P1552 [APIO2012] 派遣 - 洛谷

P3261 [JLOI2015] 城池攻占 - 洛谷


引用来源

左偏树简介-CSDN博客

还有书


每日一推

人生马拉松 - 陈奕迅

想起了元旦的小品

学习不是马拉松,而是百米赛跑

学习不是百米赛跑,而是马拉松

标签:dist,int,合并,距离,博客,节点,左偏
From: https://www.cnblogs.com/zysssss/p/18083245

相关文章

  • 【博客】K-D树
    K-D树前言kd-tree(k-dimensional树的简称),是一种对k维空间中的实例点进行存储以便对其进行快速检索的树形数据结构。主要应用于多维空间关键数据的搜索(如:范围搜索和最近邻搜索)好像跟没说一样K-D树其实是每个结点为K维数值点的二叉树每个结点将某一维划分成两部分一部分为左......
  • 【博客】【入门级】递归
    递归有部分结论代码题目来自网络在文后有链接侵删前言什么是递归?函数反复调用自身即是递归举个栗子递归我在这篇博客里写了这篇博客的链接像不像套娃举个正经栗子比如我们算\(n\)的阶乘\(f(n)\)(阶乘就是\(1*2*3*4*...*n\))以\(f(6)\)为例\(f(6)\)=>\(6\)*$......
  • 新电脑 个人博客迁移
    安装和配置所需要的软件安装Git客户端,安装过程省略,一般默认下一步    下载地址:Git客户端    这个无脑下一步即可无需配置安装nodeJS,安装过程省略,一般默认下一步    下载地址:nodeJS    配置看这位大佬教程:地址拷贝个人博客文件夹中,部......
  • 将博客搬至CSDN
    New·Object将博客搬至CSDN在数字时代的浪潮中,我始终如一地维护着自己的一方网络空间。从最初的个人网站到现在的CSDN博客,每一次的变迁都记录着我的成长与变化。今天,我想和大家分享一下将博客搬迁至CSDN的决定背后的故事,以及这个决定给我带来的一系列连锁反应。记得最初,我的博......
  • 推荐一个博客园皮肤:awescnb-theme-geek
    参考文章:我的所有做法都是参考本篇文章1.安装主题首先进入到博客后台设置:1.设置皮肤为customer,并且打开JS权限2.勾选禁用模板默认CSS3.复制粘贴配置代码(共三处)页面定制CSS代码#loading{bottom:0;left:0;position:fixed;right:0;top:0;z-index:9999;background-co......
  • 初出茅庐的小李博客之串口屏开发一个音乐控制器UI
    串口屏介绍串口屏通常指的是一种带有串口接口的显示屏,可以通过串口与其他设备进行通信和控制。这种屏幕通常具有独立的控制器和显示功能,可以直接接入主控系统,实现信息的显示和交互。开发步骤准备UI素材准备了100张音量的图标,这里面还遇到了个小问题,这么多图片如何批量......
  • Gitlab+Jenkins+Docker+Harbor+K8s集群搭建CICD平台(持续集成部署Hexo博客Demo)
    目录涉及内容:一、CICD服务器环境搭建1、docker环境安装(1)、拉取镜像,启动并设置开机自启(2)、配置docker加速器2、安装并配置GitLab(1)、创建共享卷目录(2)、创建gitlab容器(3)、关闭容器修改配置文件(4)、修改完配置文件之后。直接启动容器(5)、相关的git命令(针对已存在的文件夹)3、安装配......
  • 大家觉得2024了,还有必要搭建自己的博客吗?
    其实,这个问题我之前也纠结了很久了,现在各种自媒体平台都适合记录生活,但是,这些都是公开的,就感觉在裸奔一样,没有安全感和隐私感,而个人博客就可以规避这一点,比如可以做一个个人用的知识库,资料库,家庭照片等,只要自己记住网址,不公开,那么相当是比较安全的。你觉得呢,欢迎在评论区说下你......
  • 哇!什么情况?这么好看的个人博客首页,还这么简单,这简直就是为呆呆的我学习量身准备的
    在这个万物vue的年代,网页设计越来越框架化。上网搜个资料学习学习吧,咵咵咵,“游泳健身,vue了解一下”我只是想简单地学个html,js啊!怎么就这么复杂!曾几何时,在网上找个网页模板,纯纯的html不带一点儿复杂的东西,最多加点儿jquery。我上面加个头就能当jsp的课后作业了。虽然这种东西......
  • 博客,好久不见!
    我是22年本科毕业,目前研二在读。不得不说,计算机的行情是真卷啊,尤其是Java开发,但奈何Java的岗位还是多呀,所以打算就业方向还是Java,这就要开卷了。谈到就业,有一份实习经历真的会对正式就业有很大帮助,不论是直接暑期转正还是再面其他地方,都会是很大的优势。我最近也开始准备找实习......