【转】左偏树(可合并优先队列)

[可并堆与左偏树]

我们最常用的二叉堆,是最常用的优先队列,它可以在O(logN)内实现插入和删除最小值操作。但是对于合并两个有序的优先队列,二叉堆就显得力不从心了。

左偏树是一种可并堆(Mergeable Heap),意思是可以在O(logN)时间内完成两个堆的合并操作。左偏树(Leftist Tree),或者叫左倾树,左式树,左式堆(Leftist Heap),左堆。顾名思义,它好象是向左偏的,实际上它是一种趋于非常不平衡的二叉树结构,但却能够实现对数级的合并时间复杂度。

[左偏树的定义]

左偏树是一棵二叉树,每个节点具有四个属性:左子树(left),右子树(right),键值(key),零路径长(npl)。其中节点i的零路径长的定义为从i到i子树中最近的没有两非空个子节点的节点的路径的长度。空节点的零路径长为为-1,只有一个非空子节点的节点的零路径长为0。左偏树的节点之间除了满足堆序以外,还应满足节点左子节点的零路径长不小于右子节点的零路径长。

根据上述定义,很容易得出:对于非空节点i,满足i.npl=i.right.npl+1。还有一个性质,一棵节点数为N的左偏树,根节点的零路径长最大为?log(N+1)?-1,即O(logN),证明略。

[左偏树的合并]

左偏树的一切操作都是在合并的基础上进行的,所以要先讨论合并。

当合并两个左偏树节点a,b时,要求是这两个节点是没有包含关系的。假设a.key<b.key(如果不是这样则交换a,b),只需对a.right和b合并,合并后的结果作为新的a.right。然后更新a.npl=a.right.npl+1。

可以证明,一次合并的时间复杂度为O(log(A.size) + log(B.size))。

[左偏树的插入和删除]

对已有的左偏树a插入新的值p,只需把p构建为一个只有一个元素左偏树节点b,然后合并a,b即可。

删除左偏树的最小节点,只需把根节点删除,然后合并两个子树,作为新的根节点。

[左偏树的构建]

可以用一个队列,使左偏树的构建为O(N)。具体方法为

1 把所有元素作为一个单独左偏树节点放入队列;

2 不断取出两个队首的左偏树,合并这两个左偏树,然后放入队尾;

当队列中只剩下一个左偏树,算法结束。可以证明,时间复杂度为O(N)。

[对左偏树的比较]

左偏树可以实现二叉堆的一切功能,而且还能实现二叉堆不易实现的合并,个人认为实际编程中左偏树更有理性,不容易错。但左偏树的算法时间常数要大于二叉堆,所以不能完全代替之。

和平衡树相比,左偏树采取了与平衡树完全相反的构造策略。平衡树为了实现所有元素的快速查找,使节点尽量趋于平衡。而左偏树的目的是实现快速的查询最小值与合并操作,恰恰要让节点尽量向左偏。最优的平衡树,恰恰是最差的左偏树,而最优的左偏树,恰恰是平衡树退化的结果。

斜堆、二项堆、斐波那契堆也是可并堆实现的有效方法,而且二项堆、斐波那契堆实际中会比左偏树更快,但是在时间与编程复杂度的性价比上,左偏树有着绝对的优势。

源自:BYVOID。

时间: 2024-08-04 01:18:36

【转】左偏树(可合并优先队列)的相关文章

左偏树

概要:左偏树是具有左偏性质的堆有序二叉树,它相比于优先队列,能够实现合并堆的功能. 先仪式型orzorzozr国家集训队论文https://wenku.baidu.com/view/515f76e90975f46527d3e1d5.html 左偏树的节点定义: 1 struct node { 2 int lc, rc, val, dis; 3 } LTree[maxn]; 左偏树的几个基本性质如下: 节点的键值小于等于它的左右子节点的键值 节点的左子节点的距离不小于右子节点的距离 节点的距离等于

浅析左偏树的性质及其应用

本文是看了黄源河的论文后才写的 如果本人有哪些地方写得不对的,希望各位大佬改正ORZ 学习C++的大佬应该都会优先队列(原谅我的菜,我连priority_queue都不会拼) 左偏树说到底就是一个升级版的堆 因为左偏树拥有所有堆拥有的功能比如说插入一个节点,取出堆顶和删除堆顶 我们的左偏树的优秀到底体现在哪呢? 左偏树可以合并两个堆!!! 如果我们用普通的做法合并两个堆是需要O(N)的时间 那么如果合并操作非常多 那么堆就不在实用了 先来规定左偏树的一些概念 外节点:一个没有右儿子的节点成为外节

5.左偏树整理

目录 左偏树整理 引言 左偏树整理 整理自IOI2005 国家集训队论文 黄源河 的<左 偏 树 的 特 点 及 其 应 用> 引言 ps:优先队列的实现方式是二叉堆(完全二叉树,父亲的值大于左右两个儿子的值) 针对一些优先队列(二叉堆)合并问题的解法. 优先队列(二叉堆)可以支持三种操作 查询最大(小)值 (Query-Max(Min)) 删除最大(小)值(Delete-Max(Min)) 插入一个元素(Insert) 如果需要一次合并,想法是直接暴力合并.显然时间复杂度是$O(log n

学习笔记——左偏树

左偏树是一个堆,为了实现快速合并的操作,我们可以构造一颗二叉树,并且使右子树尽量简短 什么是左偏呢? 定义:一个左偏树的外节点是一个左子树为空或者右子树为空的节点,对于每一个点定义一个距离dist它为到它子树内外节点的最短距离. 一个合法的左偏树节点需要满足堆性以及它的右子树的dist比左子树的dist小. 为什么要这样呢? 这样右子树的dist是严格控制在logn以内的. 于是我们合并的时候,将另一个左偏树与当前左偏树的右子树合并,这样递归下去,则时间复杂度是O(logn)的. 这就是一颗左偏

【BZOJ 2809】2809: [Apio2012]dispatching (左偏树)

2809: [Apio2012]dispatching Description 在一个忍者的帮派里,一些忍者们被选中派遣给顾客,然后依据自己的工作获取报偿.在这个帮派里,有一名忍者被称之为 Master.除了 Master以外,每名忍者都有且仅有一个上级.为保密,同时增强忍者们的领导力,所有与他们工作相关的指令总是由上级发送给他的直接下属,而不允许通过其他的方式发送.现在你要招募一批忍者,并把它们派遣给顾客.你需要为每个被派遣的忍者 支付一定的薪水,同时使得支付的薪水总额不超过你的预算.另外,为

左偏树学习

左偏树(Leftist Tree)树这个数据结构内容真的很多,二叉堆,其实就是一颗二叉树,这次讲的左偏树(又叫“左翼堆”),也是树.二叉堆是个很不错的数据结构,因为它非常便于理解,而且仅仅用了一个数组,不会造成额外空间的浪费,但它有个缺点,那就是很难合并两个二叉堆,对于“合并”,“拆分”这种操作,我觉得最方面的还是依靠指针,改变一下指针的值就可以实现,要是涉及到元素的移动,那就复杂一些了.左偏树跟二叉堆比起来,就是一棵真正意义上的树了,具有左右指针,所以空间开销上稍微大一点,但却带来了便于合并的

bzoj 1455: 罗马游戏 左偏树+并查集

1455: 罗马游戏 Time Limit: 5 Sec  Memory Limit: 64 MBSubmit: 668  Solved: 247[Submit][Status] Description 罗马皇帝很喜欢玩杀人游戏. 他的军队里面有n个人,每个人都是一个独立的团.最近举行了一次平面几何测试,每个人都得到了一个分数. 皇帝很喜欢平面几何,他对那些得分很低的人嗤之以鼻.他决定玩这样一个游戏. 它可以发两种命令: 1. Merger(i, j).把i所在的团和j所在的团合并成一个团.如果

[bzoj1455]罗马游戏_左偏树_并查集

罗马游戏 bzoj-1455 题目大意:给你n个人,2种操作,m次操作:1.将i号士兵所在的集合的最小值删除 2.合并i和j两个士兵所在的团体 注释:$1\le n\le 10^6$,$1\le m \le 10^5$. 想法:又是GXZlegend讲课,可并堆中的左偏树.了解一下: 一个具有堆性质的二叉树满足任意一个节点x中,dis[lson[x]]>=dis[rson[x]],其中,dis表示当前节点一直走右儿子的最长步数.合并是递归合并,我们通过递归处理一两个节点为根节点的左偏树的合并,显

左偏树(p3377)

题目描述 如题,一开始有N个小根堆,每个堆包含且仅包含一个数.接下来需要支持两种操作: 操作1: 1 x y 将第x个数和第y个数所在的小根堆合并(若第x或第y个数已经被删除或第x和第y个数在用一个堆内,则无视此操作) 操作2: 2 x 输出第x个数所在的堆最小数,并将其删除(若第x个数已经被删除,则输出-1并无视删除操作) 输入格式 第一行包含两个正整数N.M,分别表示一开始小根堆的个数和接下来操作的个数. 第二行包含N个正整数,其中第i个正整数表示第i个小根堆初始时包含且仅包含的数. 接下来