【问题标题】:What is the best way to implement a double-ended priority queue?实现双端优先级队列的最佳方法是什么?
【发布时间】:2013-07-09 19:54:13
【问题描述】:

我想实现一个具有以下约束的双端优先级队列:

  • 需要在固定大小的数组中实现..比如说100个元素..如果数组满后需要添加新元素,则需要删除最旧的元素

  • 需要 O(1) 中的最大值和最小值

  • 如果可能,在 O(1) 中插入

  • 如果可能,删除 O(1) 中的最小值

  • 如果可能,在 O(1) 中清空/初始化状态

  • 当前数组中的元素个数计数 O(1)

我希望以上 5 种操作都使用 O(1),但在同一实现中不可能对所有操作都使用 O(1)。至少 3 个操作的 O(1) 和其他 2 个操作的 O(log(n)) 就足够了。

如果可以提供任何指向此类实现的指针,将不胜感激。

【问题讨论】:

  • 你尝试过什么吗?至少清楚 O(1) 中的清空/初始化状态对于了解基本数据结构的人来说是微不足道的:(
  • @Fallen ya 即使计数也..只需跟踪它...我只是明确说明操作和时间复杂性:),因此建议特定实现的人会对期望有清晰的认识
  • 好吧,你不能让插入提取最小值/最大值为常数或摊销常数时间,因为这意味着线性时间排序算法。所有这些都假设您的键不是整数等,而是带有比较运算符的黑盒子。

标签: algorithm data-structures big-o priority-queue heapsort


【解决方案1】:

为此有许多专门的数据结构。一种简单的数据结构是最小-最大堆,它被实现为二进制堆,其中的层在“最小层”(每个节点小于或等于其后代)和“最大层”(每个节点大于或等于它的后代。)最小值和最大值可以在 O(1) 时间内找到,并且与标准二叉堆一样,入队和出队可以在 O(log n) 时间内完成。

您也可以使用interval heap data structure,这是另一个专门的任务优先级队列。

或者,您可以使用两个优先级队列 - 一个以升序存储元素,一个以降序存储元素。每当您插入一个值时,您就可以将元素插入两个优先级队列,并让每个队列存储一个指向另一个队列的指针。然后,每当您将最小值或最大值出列时,您都可以从另一个堆中删除相应的元素。

作为另一种选择,您可以使用平衡二叉搜索树来存储元素。然后可以在 O(log n) 时间内找到最小值和最大值(如果缓存结果,则为 O(1)),并且可以在 O(log n) 时间内完成插入和删除。如果您使用的是 C++,您可以使用 std::map 进行此操作,然后使用 begin()rbegin() 分别获取最小值和最大值。

希望这会有所帮助!

【讨论】:

  • 谢谢,最大的帮助是在 C 或 C++ 中干净地实现最小-最大堆
  • @Medicine- 我用其他几个解决方案更新了我的答案。最后一个解决了一个简单的 C++ 想法。
【解决方案2】:

binary heap 将在O(log n)O(1) 中为您提供插入和删除最小值。

唯一棘手的部分是在数组已满后删除最旧的元素。为此,保留另一个数组:

time[i] = at what position in the heap array is the element 
          added at time i + 100 * k. 

每 100 次迭代,您递增 k

然后,当数组第一次填满时,你删除heap[ time[0] ],当它第二次填满时你删除heap[ time[1] ],...,当它第100次填满时,你换行周围并再次删除heap[ time[0] ] 等等。当它填满kth 时,你删除heap[ time[k % 100] ](100 是你的数组大小)。

确保在插入和删除元素时也更新 time 数组。

如果您知道它的位置,可以在O(log n) 中删除任意元素:只需将其与堆数组中的最后一个元素交换,然后筛选您已交换的元素。

【讨论】:

  • 我们如何在 O(1) 中从二进制堆中找到最小值和最大值,我认为我们只能在 O(1) 中找到它们中的一个,具体取决于它的最小或最大堆。
  • @Medicine - 保留两个堆,或查看 templatetypedef 的答案。
  • 好的,所以要删除 min..我可以在 O(log(n)) 中从 min heap 中删除..从 max heap 中删除相同的元素需要额外的努力
  • @Medicine - 相同,O(log n)2*O(log n) = O(log n)。您可以通过保持类似于time 的数组来查看一个元素在另一个元素中的位置。但是,如果您使用最小-最大堆,它可能会减少工作量。
  • 正在考虑最小最大堆,但需要参考实现
【解决方案3】:

如果您绝对需要 max 和 min 为 O(1),那么您可以做的是创建一个链表,在其中不断跟踪 min、max 和 size,然后将所有节点链接到某种树形结构,可能是堆。最小值、最大值和大小都是恒定的,并且由于找到任何节点都在 O(log n) 中,因此插入和删除都是 log n。清算将是微不足道的。

【讨论】:

    【解决方案4】:

    如果您的队列是固定大小的,那么 O-notation 是没有意义的。任何 O(log n) 甚至 O(n) 操作本质上都是 O(1),因为 n 是固定的,所以您真正想要的是一种对给定数据集快速的算法。可能两个并行的传统堆优先级队列就可以了(一个为高,一个为低)。

    如果您对自己拥有的数据类型有更多了解,您也许可以制作出更特殊用途的东西。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2010-11-11
      • 1970-01-01
      • 2016-01-19
      • 1970-01-01
      • 1970-01-01
      • 2017-02-08
      • 2012-11-26
      • 1970-01-01
      相关资源
      最近更新 更多