有稳定的堆吗?


32

是否存在支持以下操作的优先级队列数据结构?

  • Insert(x,p):添加优先级为p的新记录x
  • StableExtractMin():返回并删除具有最低优先级的记录,并按插入顺序断开联系

因此,在Insert(a,1),Insert(b,2),Insert(c,1),Insert(d,2)之后,StableExtractMin的序列将返回a,然后c,然后b,然后d。

显然,可以通过将对存储为实际优先级来使用任何优先级队列数据结构,但是我对类似于稳定排序的数据结构没有显式存储插入时间(或插入顺序)感兴趣。。(p,time)

等效地(?):是否有稳定版本的heapsort不需要额外空间?Ω(n)


我认为您的意思是“ a,然后c,然后b,然后d”?
罗斯·斯尼德

具有记录链接列表的堆+优先级指向对应链接列表的平衡二叉树无法正常工作?我想念什么?
Aryabhata

Moron:那是显式存储插入顺序,这正是我要避免的。我澄清了问题陈述(并修正了罗斯的错字)。
杰夫斯

Answers:


16

Bently-Saxe方法提供了一个相当自然的稳定优先级队列。

将数据存储在一系列已排序的数组。 大小为。每个数组还维护一个计数器。数组条目包含数据。A0,,AkAi2iciAi[ci],,Ai[2i1]

对于每个,中的所有元素都比所有元素添加得更近,并且在每个元素中按值排序,并且通过将较旧的元素放在较新的元素之前来打破联系。请注意,这意味着我们可以合并和并保留此顺序。(对于合并期间的关系,从获取元素。)iAiAi+1AiA i + 1 A i + 1AiAi+1Ai+1

要插入值,找到最小的,使包含0个元素,合并和,将其存储在并适当设置。i A i A 0A i 1 x A i c 0c ixiAiA0,,Ai1xAic0,,ci

要提取最小值,请找到最大的索引,以使中的第一个元素在所有上最小,并递增。 [ Ç ] Ç iAi[ci]ici

根据标准参数,这将为每个操作提供摊销时间,并且由于上述顺序而稳定。O(logn)

对于插入和提取的序列,这将使用数组条目(不要保留空数组)加上个簿记数据字。它没有回答Mihai的问题版本,但是它表明稳定的约束并不需要很多空间开销。特别是,它表明在所需的额外空间上没有下界。n O log n Ω n nnO(logn)Ω(n)

更新: Rolf Fagerberg指出,如果我们可以存储空值(非数据),则可以将整个数据结构打包为大小为的数组,其中是到目前为止的插入数。ñnn

首先,请注意,我们可以顺序将打包到一个数组中(首先使用,如果非空则使用,依此类推)。它的结构完全由的二进制表示形式编码,是到目前为止插入的元素数。如果的二进制表示在位置处为1 ,则将占据数组位置,否则它将不占据任何数组位置。A k A k 1 n n i A i 2 iAk,,A0AkAk1nniAi2i

插入,数组的长度增加1,我们可以使用现有的就地稳定合并算法合并和新元素。A 0A inA0,,Ai

现在,我们使用空值的地方是摆脱计数器。在,我们存储第一个值,然后存储空值,然后存储其余的值。在提取分钟期间,我们仍然可以通过检查来找到要在时间内提取的值。当我们在找到该值时,我们将设置为null,然后对进行二进制搜索以找到第一个非空值并交换和。ciAici2ici1O(logn)A0[0],,Ak[0]Ai[0]Ai[0]AiAi[ci]Ai[0]Ai[ci]

最终结果:整个结构可以用一个数组来实现,该数组的长度随每次插入而增加,并用一个计数器计数插入的次数。n


1
这会在O(n)提取后的给定瞬间使用潜在的O(n)额外空间,不是吗?此时,您最好也存储优先级……
Mehrdad 2014年

10

我不确定您的约束是什么;符合以下条件?将数据存储在一个数组中,我们将其解释为隐式的二叉树(如二叉堆),但数据项位于树的底层而不是其内部节点。树的每个内部节点存储从其两个子节点复制的较小的值。如果有关系,请复制左孩子。

要找到最小值,请查看树的根。

要删除元素,请将其标记为已删除(延迟删除)并沿树传播(到根目录路径上的每个节点,该节点持有已删除元素的副本,都应替换为其另一个子节点的副本)。维护已删除元素的数量,如果它占所有元素的比例过大,则重新构建结构以保留元素在最底层的顺序-重建需要线性时间,因此该部分仅向组件添加固定的摊销时间操作复杂度。

要插入元素,请将其添加到树底行的下一个空闲位置,然后将路径更新为根。或者,如果底部行已满,则将树的大小加倍(再次使用分期摊销参数;请注意,这部分与标准二进制堆的数组增长而不再需要重建时没有什么不同)。

但是,这并不是对Mihai更严格的问题的答案,因为它使用的内存是真正的隐式数据结构的两倍,即使我们忽略了延迟处理删除的空间成本。


我喜欢这个。就像使用常规隐式树min-heap一样,由于缓存效应,即使3元或4元隐式树也可能更快(即使您需要进行更多比较)。
乔纳森·格雷尔

8

以下是对您的问题的有效解释:

您必须将N个键存储在A [1..N]的数组中,而没有辅助信息,因此您可以支持:*插入键*删除最小值,如果存在多个最小值,则该元素将选择最早插入的元素

鉴于大多数隐式数据结构都以某些元素的本地顺序发挥作用,对位进行编码很困难,因此这看起来很难。在这里,如果多个家伙相等,则必须保留他们的顺序,因此不可能有这样的技巧。

有趣。


1
我认为这应该是评论,而不是答案,因为它并不能真正回答原始问题。(您可以删除它并将其添加为评论。)
Jukka Suomela 2010年

5
是的,这个网站有点荒谬。我们有声誉,奖金,奖励以及各种我无法弄清楚的评论方式。我希望这看起来不像是儿童游戏。
Mihai 2010年

1
我认为他需要更多代表才能发表评论。那就是问题所在。
Suresh Venkat 2010年

@Suresh:哦,对,我不记得了。我们实际上应该如何处理这种情况(即,新用户需要在回答问题之前要求澄清)?
Jukka Suomela

2
没有捷径。我在MO上经常看到这种情况。如果我认为它是Mihai,那么Mihai将毫无困难地获得代表:)
Suresh Venkat 2010年

4

简短答案:您不能。

答案略长:

您将需要额外的空间来存储条目的“年龄”,这将使您能够区分相同的优先级。而且,您需要空间来获取允许快速插入和检索的信息。加上您的有效负载(值和优先级)。Ω(n)Ω(n)

而且,对于您存储的每个有效负载,您都可以“隐藏”地址中的某些信息(例如,表示Y早于X)。但是在该“隐藏”信息中,您将隐藏“年龄”或“快速检索”信息。不是都。addr(X)<addr(Y)


不精确的片状伪数学的答案很长:

注意:如上所述,第二部分的末尾是粗略的。如果有一些数学专家可以提供更好的版本,我将不胜感激。

让我们考虑一下X位机器(例如32或64位)上涉及的数据量,其中记录(值和优先级)为机器字。P

您有一组可能的记录,这些记录的一部分是有序的:和但无法比较和。(a,1)<(a,2)(a,1)=(a,1)(a,1)(b,1)

但是,您希望能够根据插入记录的时间来比较记录集中的两个不可比较的值。因此,这里有另一组值:已插入的值,并且您要用部分顺序对其进行增强:如果在之前插入。X<YXY

在最坏的情况下,你的记忆将充满形式的记录(用对于每一个不同的),所以你必须以决定哪一个去当插入时间完全依赖首先出来。(?,1)?

  • 插入时间(相对于仍在结构中的其他记录)需要位信息(具有P字节有效负载和可访问内存字节)。Xlog2(P)2X
  • 有效负载(记录的值和优先级)需要机器字信息。P

这意味着您必须为存储的每个记录以某种方式存储额外的信息位。这就是为记录。Xlog2(P)O(n)n

现在,每个内存“单元”为我们提供多少信息?

  • W位数据(是机器字的宽度)。W
  • X位地址。

现在,我们假设(有效载荷至少为一个机器字宽(通常为一个八位位组))。这意味着,因此我们可以将插入顺序信息放入单元格的地址中。这就是堆栈中发生的事情:地址最低的单元格首先进入堆栈(最后退出)。P1Xlog2(P)<X

因此,要存储所有信息,我们有两种可能性:

  • 将插入顺序存储在地址中,并将有效负载存储在内存中。
  • 将两者都存储在内存中,并保留地址以用于其他用途。

显然,为了避免浪费,我们将使用第一种解决方案。


现在开始操作。我想您希望拥有:

  • Insert(task,priority),时间复杂度为。O(logn)
  • StableExtractMin()具有时间复杂度的。O(logn)

让我们看一下:StableExtractMin()

真正通用的算法是这样的:

  1. 在找到具有最低优先级和最短“插入时间”的。O(logn)
  2. 从的结构中删除它。O(logn)
  3. 把它返还。

例如,在堆的情况下,其组织会稍有不同,但工作是相同的:1.在找到min记录 2.在从结构中删除它 。3.修复一切,以便下次#1和#2仍为即“修复堆”。这需要在“ O(log n)”中完成。4.返回元素。0(1)O(1)O(1)

回到一般算法,我们看到要找到时间的记录,我们需要一种快速的方法来在候选对象中选择正确的一个(最坏的情况是,内存是充分)。O(logn)2(Xlog2(P))

这意味着我们需要存储位信息才能检索该元素(每个位将候选空间一分为二,因此我们有分,这意味着时间复杂度)。Xlog2(P)O(logn)O(logn)

这些信息位可能存储为元素的地址(在堆中,最小值位于固定地址),或者例如,使用指针(在二进制搜索树中(带有指针),您需要遵循以获得平均值)。O(logn)

现在,当删除该元素时,我们需要增加下一个min记录,以便它具有正确的信息量以允许下次进行检索,也就是说,它具有位区别于其他候选人的信息。O(logn)Xlog2(P)

也就是说,如果尚没有足够的信息,则需要添加一些信息。在(非平衡)二进制搜索树中,信息已经存在:您必须将NULL指针放在某个位置以删除元素,并且无需任何进一步操作,就可以在时间搜索BST 。平均。O(logn)

在此之后,它还是有点粗略的,我不确定如何制定。但是我有一种强烈的感觉,即您集合中的每个其余元素都需要具有位信息,这些信息将有助于查找下一个分钟并用足够的信息对其进行扩充,以便可以在下一次时间。O l o g n Xlog2(P)O(logn)

插入算法通常只需要更新此信息的一部分,我认为快速执行它不会花费更多(从内存角度而言)。


现在,这意味着我们需要为每个元素存储更多的信息位。因此,对于每个元素,我们都有:Xlog2(P)

  • 插入时间位。Xlog2(P)
  • 有效载荷机器字。P
  • “快速搜索”信息,位。Xlog2(P)

由于我们已经使用内存内容来存储有效负载,并使用地址来存储插入时间,因此我们没有剩余空间来存储“快速搜索”信息。因此,我们必须为每个元素分配一些额外的空间,以便“浪费”额外空间。Ω(n)


您真的打算让您的答案CW吗?
Suresh Venkat 2010年

是。我的回答不是100%正确,就像其中所说的那样,即使我不再使用SO或其他方法,如果有人可以纠正它,那将是一个很好的选择。知识应该共享,知识应该是可变的。但是也许我误解了CW的用法,如果是的话,请告诉我:)。编辑:糟糕,实际上我只是发现我不会从CW帖子中获得任何代表,并且该内容以任何方式均已获得CC-wiki许可...太糟糕了:)。
苏珊娜·杜彭(SuzanneDupéron)2010年

3

如果将优先级队列实现为平衡的二叉树(一种流行的选择),则只需确保在向树中添加元素时,该元素就会插入到具有相同优先级的所有元素的左侧。
这样,插入顺序被编码在树本身的结构中。


1
但这为指针增加了O(n)空间,我认为发问者想避免的是什么?
杰里米

-1

我认为那不可能

具体案例:

       x
    x    x
  x  x  1  x
1  x  

所有x> 1的最小堆

堆化最终将给像这样的选择

       x
    1    1
  x  x  x  x
x  x  

现在哪个1传播到根?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.