堆的进化之旅5-Relaxed Heap松弛堆


堆的进化之旅的分支——松弛堆。
为记录算法研讨课精彩报告而生。

1987年,Michael L. Fredman 与 Robert E. Tarjan志得意满地公开发表了斐波那契堆,分操作复杂度都优化到了那时也是现在为止最好的程度:两个O(1)加1个O(log n)。他们满心以为已经到达了堆进化之旅的尽头,但可惜在这趟旅途的终点已经被Driscoll·Gabow占据。1988年,前后脚,Driscoll·Gabow发表了松弛堆,复杂度分析上与斐波那契堆处于同一水平,但他的常数项却小于斐波堆。接下来几年中,原本为推广斐波那契堆的时间,却屡屡让松弛堆出尽风头。那什么是松弛堆呢?

松弛堆

OperationLinked ListBinary HeapBinomial HeapFibonaaci HeapRelaxed Heap
PushO(1)O(log n)O(1) amortizedO(1) amortizedO(1) amortized
Pop_minO(n)O(log n)O(log n)O(log n) amortizedO(log n) amortized
Decrease_keyO(1)O(log n)O(log n)O(1) amortizedO(1) amortized

创造一个标记:好节点或坏节点。
如果一个节点是根节点或者符合堆有序(比父亲节点要大),那么就是好节点,否则是坏节点。
还有active(活跃)标记,简单地说,在更新关键字之后呢,它有可能不符合堆有序了,是潜在的威胁,那么我们把它标记为活跃节点,意思是说,注意这个嫌疑犯,别让它干坏事,这样看来和坏小子bad node又有区别,但实际上我们姑且认为它们是一样的。
同时有结论:所有坏节点都是活跃节点。
在这里插入图片描述
为介绍具体操作,我们创造4种标记符号,

  1. 孩子是好的,那么标记上指向父亲的箭头;
  2. 孩子是新产生的活跃节点,那么把与父亲连接的边画成虚线;
  3. 如果孩子的状态未知,就什么也不做;
  4. 孩子是活跃的坏节点,即不满足堆特性,标记上叉;
    在这里插入图片描述
    就其具体操作来说,可以把松弛堆分为两类,我们首先来看Rank松弛堆,它的定义有两项,即对每一个数字 r 来说,有r 个孩子的节点最多有 1 个active 顶点,并且对每一个active顶点,都是最后一个孩子。为了满足这个定义,当出现一些不符合的情况时,我们可以执行四种操作,将其转化为Rank松弛堆,分别是pair transformation,也就是针对一对相同堆的操作;clean,也就是清理操作;active sibling transform,针对两个活跃节点的操作;good sibling transform,针对其相邻节点是好节点的情况。

Rank Relaxed Heap

在这里插入图片描述

Pair transformation

在这里插入图片描述

Clean

在这里插入图片描述
接下来是clean清理操作,因为从图中我们可以看出活跃节点x并没有如定义里指出那样是最后一个节点,这时我们就可以找到它的最后一个兄弟节点q’的最后一个儿子节点,然后将他们进行交换,实现将活跃节点变成最后一个节点的要求,这里我们也只是进行了1个交换操作,因此时间复杂度也为O(1)

Active sibling transformation

在这里插入图片描述
第三个是active sibling transformation操作,此操作针对有两个活跃节点的情况,他们是兄弟节点,且有r个孩子的节点a位于倒数第二个孩子节点位置,有r+1个孩子节点的s节点位于最后一个孩子节点的位置,这也是不符合定义要求的,因为定义要求是每棵树只准有一个活跃的子节点,为了调整,我们将以p,a,s为根节点的子树拆下,在这里我们注意到一个细节,因为a是坏节点,所以a的键值大于p的键值,因此将a子树和p子树进行合并,因为都有r个孩子,并且a当父亲节点,因为a的键值比较小。合并a和p后,a和s都是有r+1个孩子的节点,因此我们就可以仿照之前的操作,将a和s合并,选取他们两个中较小的作为父节点c,连接到节点g下,同样的,因为是新产生的活跃节点,因此我们以虚线连接c和g,这都是O(1)操作,复杂度为O(1)

Good sibling transformation

在这里插入图片描述
最后是good sibling transformation操作,顾名思义,因为活跃节点a的兄弟节点s是好节点,这里可以分两种情况来讨论,(1)若s节点的最后一个孩子节点c是坏节点,那么就将a和c拆下,并仿照上述操作,将其合并为有r+1个孩子节点的c,这时,p和s节点的组合使得p也有r+1个孩子,因此p和c也可进行合并(2)若s节点的最后一个孩子节点是好节点,其实对这个情况我们并不陌生,即刚刚展示的clean操作,最终可以看出这一步操作仍为常数步,时间复杂度为O(1)

Run Relaxed Heap

在这里插入图片描述
在松弛堆中另一类是Run松弛堆,它满足两个条件:活跃点数目,二项式树的个数,因此为了保证堆为Run松弛堆,操作可分成Singleton transformation操作和Run transformation这里强调两个概念, singleton 是不在一个运行里面的一个单独的活动节点,run是一个活跃的兄弟序列。下面我们来看具体操作:

Singleton transformation

(1)

Singleton transformation有3种情况,我们先来看第一种,PPT中这张图是不是很眼熟,没错,和pair transformation相同,只不过现在是以单个活跃节点的角度来进行操作,而pair transformation是以子树的角度来来进行操作,类似的,最终复杂度为O(1)。

我们这里采用了一个很重要的思想来讲解,类似于NP完全问题的证明,想办法把待证明的问题归约到已经证明的问题上即可,在这里我们想办法把所有情况转换成为(1)来求解。
在这里插入图片描述

(2.1)

第二种情况又根据c是否为好的节点分为两种,若c是a的兄弟节点s的最后一个节点并且是坏节点,观察图,这个情况和之前的pair transformation类似,我们可以将c当作pair transformation中的a节点,这样,这种情况就和pair transformation十分类似了,我们只需按照类似于pair transformation中对a和a’的操作来对c和a’进行类似的操作即可,时间复杂度依然为O(1)
在这里插入图片描述
左图中用灰色铅笔圈出的p, s, c部分,把它看作(1)里面左边的g, p, a即可。于是问题转化为(1)。

(2.2)

若c是a的兄弟节点s的最后一个节点并且是好节点,正如图片上部所示的图,我们发现和clean操作类似,因此我们进行类似于clean的操作,将a和c进行互换即可,最后所需复杂度和clean操作相同,为O(1)
在这里插入图片描述
这样我们就把问题转化成为了(2.1)

(3)

下面是Singleton transformation的第三种情况,这时,a和a’都是活跃节点且不为最后一个孩子,我们可就c和c’是否是好的节点进行分类,两两组合就会有4种情况。(我们首先来看c和c’都是好的的情况,没错,这时两棵子树的情况和clean的操作都类似,我们可以对两棵树分别进行clean操作;再来看c和c’一好一坏的情况,这种情况我们也是十分的熟悉,就和Singleton transformation的2.1操作类似,如灰色的圈所示,我们可以将其看作2.1中那颗较小的子树,从而执行类似于2.1的操作;对于c和c’都是坏节点的情况,相信大家已经找到了规律,)最后发现这就是2.2的情况的进阶版,
在这里插入图片描述
还可以把步子迈小一点,以左边2个图为例,下部用灰色铅笔全出的部分看作是(2)里面的右边就好,这样将问题转化为(2),具体对应关系呢

(c, c’)good, goodgood, badbad, goodbad, bad
transforma<->c, a’<->c’2.22.22.1

Run transformation

(1)

接下来是Run transformation,就a是否为最后一个节点来分情况讨论。在这里,我们先讨论a是最后一个节点的情况,看到有r-1个节点的p和t,自然而然想到将她们两个进行合并,形成有r个节点的树,从t是坏节点也可以看出,p比t大,因此合并时我们以t为父亲,之后,两个均有r个节点的t和a就可以进行合并操作,选取他们中较小的一个为父节点,连接至g下,同样的,是新生成的活跃节点,所以连线为虚线。
在这里插入图片描述

(2)

然后是第二种情况,a不是最后一个节点的情况,这时我们将p的所有子节点拆下后,可以看出,c和p都有r-1个子节点,先将他们进行合并,形成有根节点度为r的树,再与d合并,形成有r+1个孩子节点的树。再将t和s节点合并,形成有r个孩子节点的树,然后再与a节点合并,形成有r+1个孩子节点的树,取t和a中较小的为父节点,然后就可以与同为r+1个孩子的节点p进行合并,从而完成操作,使得活跃节点到最后一个。

在这里插入图片描述
还可以这么看,对于顶点p来说,t和a是坏孩子,s是好孩子,它们之间的大小关系一目了然,这样先合并t和s,再将合并之后的t与a合并,去较小者为新父亲节点,连在p的后面,至于c和d,可以看作是打酱油的存在。

至此,所有操作都已介绍完成,松弛堆复杂就复杂在针对多种不同的情况进行优化处理,写成伪代码就如图中,最终复杂的算法步骤产生了简单的时间复杂性。在这里插入图片描述
翻译成一个流程图
在这里插入图片描述
在堆的进化之旅尽头,我们根据论文复现了这几种数据结构,它们应用在迪杰斯特拉算法中获得的效率就如这四幅图所示,结果恰如之前分析,Relaxed Heap 和 Fibonacci Heap效率最高。
在这里插入图片描述

尾声

我们合并的时候,总是对度数相等的树来下手。这一系列所有堆都是这样,这也对应了Fibonacci 数列的经典公式 f(i) = f(i-1) + f(i-2)

  • 3
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值