最大堆中的第K个最大元素

时间:2015-07-16 13:47:16

标签: algorithm data-structures heap pseudocode

我试图想出一些东西来解决以下问题:

  

给定一个表示为数组的max-heap,返回第k个最大元素而不修改堆。我被要求在线性时间内完成,但被告知可以在日志时间内完成。

我想到了一个解决方案:

使用第二个最大堆并用k或k + 1值填充它(宽度优先遍历到原始值)然后弹出k个元素并获得所需的值。我想这应该是O(N + logN)= O(N)

是否有更好的解决方案,可能是在O(logN)时间?

4 个答案:

答案 0 :(得分:7)

max-heap可以有很多种方式,更好的情况是完整的排序数组,而在其他极端情况下,堆可以有一个完全不对称的结构。

这里可以看到: enter image description here

在第一种情况下,第k个lagest元素位于第k个位置,您可以使用堆的数组表示在O(1)中计算。 但是,通常,您需要检查(k,2k)元素,并对它们进行排序(或与另一个堆进行部分排序)。据我所知,它是O(K·log(k))

算法:

Input:
    Integer kth <- 8
    Heap heap <- {19,18,10,17,14,9,4,16,15,13,12}

BEGIN
    Heap positionHeap <- Heap with comparation: ((n0,n1)->compare(heap[n1], heap[n0]))

    Integer childPosition
    Integer candidatePosition <- 0
    Integer count <- 0
    positionHeap.push(candidate)
    WHILE (count < kth) DO
        candidatePosition <- positionHeap.pop();
        childPosition <- candidatePosition * 2 + 1
        IF (childPosition < size(heap)) THEN
            positionHeap.push(childPosition)
            childPosition <- childPosition + 1
            IF (childPosition < size(heap)) THEN
                positionHeap.push(childPosition)
            END-IF
        END-IF
        count <- count + 1
    END-WHILE
    print heap[candidate]
END-BEGIN

EDITED

我在Frederickson找到了“最小堆中的最佳选择算法”: ftp://paranoidbits.com/ebooks/An%20Optimal%20Algorithm%20for%20Selection%20in%20a%20Min-Heap.pdf

答案 1 :(得分:4)

不,没有O(log n)-time算法,通过简单的单元探测下限。假设k是2的幂(不失一般性)并且堆看起来像(min-heap incoming,因为它更容易标记,但没有真正的区别)

      1
   2     3
  4 5   6 7
.............
permutation of [k, 2k).

在最坏的情况下,我们必须读取整个排列,因为堆没有强加的顺序关系,并且只要找不到k,它就可以在尚未检查的任何位置。这需要时间Omega(k),匹配templatetypedef发布的(复杂!)算法。

答案 2 :(得分:2)

据我所知,没有简单的算法来解决这个问题。我所知道的最好的算法是由弗雷德里克森引起的并不容易。您可以查看论文here, but it might be behind a paywall.它在时间O(k)中运行,并且声称这是最佳时间,因此我怀疑不存在对数时间解决方案。

如果我找到比这更好的算法,我一定会让你知道。

希望这有帮助!

答案 3 :(得分:1)

数组中的最大堆:element at i is larger than elements at 2*i+1 and 2*i+2i从0开始)

您需要另一个最大堆(insertpopempty),其元素对(value, index)value排序。伪代码(没有边界检查):

input: k
1. insert (at(0), 0)
2. (v, i) <- pop and k <- k - 1
3. if k == 0 return v
4. insert (at(2*i+1), 2*i+1) and insert (at(2*+2), 2*+2)
5. goto 2

运行时评估

  • 在(i)的数组访问:O(1)
  • 插入堆:O(log n)
  • 插入4.最多需要log(k),因为对的堆大小最多为k + 1
  • 声明3.最多达到k次
  • 总运行时间:O(k log k)