【问题标题】:Queue data structure supporting fast k-th largest element finding支持快速第k大元素查找的队列数据结构
【发布时间】:2012-09-21 14:00:19
【问题描述】:

我遇到了一个问题,需要一个支持快速第 k 个最大元素查找的队列数据结构。

这个数据结构的要求如下:

  1. 队列中的元素不一定是整数,但它们必须是相互可比的,即我们可以通过比较两个元素来判断哪个更大(它们也可以相等)。

  2. 数据结构必须支持入队(添加尾部元素)和出队(移除头部元素)。

  3. 可以快速找到队列中第k大的元素,请注意k不是常数。

  4. 您可以假设操作 enqueue 、 dequeue 和第 k 个最大元素查找都以相同的频率发生。

我的想法是使用修改后的平衡二叉搜索树。该树与普通平衡二叉搜索树相同,只是每个节点i都增加了另一个字段ni,ni表示数字根节点i 的子树中包含的节点数。上述操作支持如下:

为简单起见,假设所有元素都是不同的。

Enqueue(x):x首先插入到树中,假设对应的节点是nodet,我们追加pair(x,pointer to node t) 到队列中。

Dequeue:假设(e1, node1)是头部元素,node1是指向对应的树的指针e1。我们从树中删除 node1 并从队列中移除 (e1, node1)。

K-th最大元素查找:假设根节点是noderoot,它的两个子节点是nodeleft和noderight (假设都存在),我们比较 K 和 nroot ,可能会出现三种情况:

  1. 如果 Kleft 我们在 nroot 的左子树中找到第 K 个最大的元素;

  2. 如果 K>nroot-nright 我们找到 (Knroot+nright)-nroot的右子树中的最大元素;

  3. 否则nroot就是我们想要的节点。

这三个操作的时间复杂度都是 O(logN) ,其中 N 是当前队列中的元素个数。

如何加快上述操作?使用什么数据结构以及如何使用?

【问题讨论】:

  • 这似乎是一个人为的容器(找到第 k 个元素但不删除它,所有操作的频率相同) - 您能否提供有关您要解决的潜在问题的更多信息?如果您真的需要所有这些要求,我认为您已经找到了可靠的解决方案。
  • @Mark B,我正在处理一个数据流,我想知道滑动窗口中当前的第 k 个最大元素。其实我想找到前k个元素。
  • 这可能有点难读,但boost::multiIndex 可以帮助解决这个问题。
  • @outlaw,谢谢。我想我同意马克 B 的观点。如果k 是常量(不是),您可以使用堆来跟踪第 k 个元素。这将允许 O(1) 找到第 k 个元素和 O(logN) 用于入队/出队。
  • 值得将二叉搜索树和队列组合成一个单一的结构。只需为每个节点使用一个额外的指针,它应该指向 FIFO 顺序中的下一个节点。

标签: java c++ algorithm data-structures


【解决方案1】:

注意 - 你不可能比O(logn) 做得更好,充其量你需要“选择”你最关心的操作。 (否则,您可以通过将数组提供给 DS 并查询第一个、第二个、第三个、...第 n 个元素来对 O(n) 进行排序)

  • 使用skip list 代替平衡BST 作为排序结构 可以减少出列复杂性到O(1)平均情况。它确实 不影响任何其他操作的复杂性。
    要从跳过列表中删除 - 您需要做的就是使用队列头部的指针到达元素,然后沿着链接向上并删除每个链接。预计需要删除的节点数为 1 + 1/2 + 1/4 + ... = 2。
  • find Kth 可以在O(logK)中实现,方法是从最左边的节点(而不是根节点)开始,一直向上直到你发现“需要更多的儿子”,然后处理公正的就像问题中的算法一样,找到节点作为根。虽然它在渐近复杂度方面更好 - 常数因子是双倍的。

【讨论】:

    【解决方案2】:

    我发现了一篇有趣的论文:

    在 VLDB 2008 中发布并被 71 引用的关于不确定流的滑动窗口 Top-k 查询。

    https://www.cse.ust.hk/~yike/wtopk.pdf

    VLDB是数据库研究领域最好的会议,引用次数证明数据结构确实有效。

    这篇论文看起来挺难的,但是如果你真的需要改进你的数据结构,我建议你阅读这篇论文或者这篇论文的参考页面中的论文。

    【讨论】:

      【解决方案3】:

      您也可以使用finger tree

      例如,优先级队列可以通过使用树中子节点的最低优先级标记内部节点来实现,或者可以通过节点中的叶子数标记节点来实现索引列表/数组孩子们。 Finger tree可以提供摊销O(1) cons、reversing、cdr、O(log n) append和split;并且可以适应索引或有序序列。

      另请注意,作为纯函数结构,这使其成为并发使用的不错选择。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-04-07
        • 1970-01-01
        • 2013-06-03
        • 2014-06-23
        • 1970-01-01
        • 2013-10-03
        • 1970-01-01
        相关资源
        最近更新 更多