删除O(1)中的最大值并插入O(logn)

NoN*_*eY0 8 algorithm data-structures

设计一个支持n元素集上的以下操作的数据结构:

  1. 及时插入 O(lg n)
  2. 最大限度地删除O(1)时间.请注意,删除max占用O(lg n)传统堆.

我的方法:

我决定保留一个单独的数组,它将跟踪将超越根的潜在后继者(这是最大堆中的最大值); 一旦删除.因此,如果你删除max,O(1)我将在我的数组中查找下一个合适的后继者(我假设我将智能地设置).

有人有更好的方法吗?尽量坚持使用堆.这不是一个家庭工作的问题,我正在准备面试,而且它来自Skiena的书.

rru*_*fai 5

您的要求非常具体——您只对这两个操作感兴趣:插入O (lg n ) 和 deleteMin O (1)。

没有一个已知的堆结构满足您的特定约束。相反,最好的结构(尽管理论上——有些人称之为银河结构)是Brodal Queue,它在O (1) 最坏情况下执行所有堆操作,除了 deleteMin 仍然需要O (lg n ) 最坏情况-案例时间。所有其他已知的结构也好不到哪里去。

由于您只对这两个操作感兴趣,因此您可能能够摆脱只处理这两个操作的自定义结构,因为您不必担心减少键,合并等更多一般堆结构不用担心。

保持双重结构DL,包含:

  1. 排序动态数组d,从中你可以做一个查找Ô(LG ñ由二进制搜索)的时间。
  2. 排序链表L,从中您可以在O (1) 时间内执行 deleteMin并在O (1) 最坏情况下给定后继(或前驱)引用进行插入。
  3. 最近插入到L但尚未与D同步的元素的排序列表T

此外,在L 中的每个条目与其对应的DT 中的条目之间保持链接,反之亦然。此外,您需要为D 的每个条目维护一个位,指示它是否已在L 中被删除。为了以后做之间的质量同步d大号,你可能还需要跟踪缺失的数量,d,从大号自上次同步。您可以在以下不变量之后进行同步:

  • 不变量 1: d + | T | < n

被侵犯。这样同步时间在n 中保持线性并且您始终可以保证 | T | 和d在可控范围内。

因此,要将新元素e插入DL,首先在D中为其后继(前任)执行 find( e )并在T 中再次搜索其后继并获取较大后继(较小前任)的引用并使用插入L并将e添加到T并维护引用。插入e 后,我们检查是否违反了不变量 1。如果是这样,我们触发同步。

同步本质上合并了TD的内容,同时将标记为已删除的元素移除到新的D结构中。这可以在 |T| 的时间线性内完成 + |D| = O(n)。在另一个线性时间内,可以更新LD之间的参考。这种大规模同步的成本可以通过插入和删除来分配(摊销)。因此,这些成本只是摊销成本。

要执行 deleteMin,您只需删除L的头部并使用其到D 的反向链接将其在D 中的相应条目标记为已删除并增加d

观察 1:请注意,deleteMin 将始终删除最小元素,因为L始终是最新的。

观察 2D并不总是与L同步。它可能有一些删除的元素(如此标记)和一些插入的元素只能在T 中找到。

通过观察 2,我们需要在某个时刻安排DL的同步,以维持O (lg n ) 查找和插入成本。每当违反不变量 1 时都会执行此操作。

我稍微掩盖了一个讨厌的问题如下:如何在对数时间内插入T,同时仍然能够在同步期间进行线性扫描。只有某种平衡的树才能实现这一点。我曾想过将 T 的大小限制为对数大小的想法,但是当完成足够数量的插入以触发同步时,这会增加同步的摊销成本。似乎一种线程平衡树甚至跳过列表应该在这里有所帮助。

随意批评并提出改进建议,因为我还没有证明或实施这里的所有断言。

更新:正如@delnan 所指出的,这些成本已摊销。我已经更新了描述以强调这一事实并为清晰起见进行了修改。也许,通过一些技巧可以消除摊销,但在这种情况下,我们最终会得到另一个银河结构。