问题 heapq库中函数的时间复杂度是多少


我的问题来自下面的leetcode解决方案,我无法理解为什么会这样 O(k+(n-k)log(k))

补充:也许复杂性不是那样,实际上我不知道时间的复杂性 heappush() 和 heappop()

# O(k+(n-k)lgk) time, min-heap
def findKthLargest(self, nums, k):
    heap = []
    for num in nums:
        heapq.heappush(heap, num)
    for _ in xrange(len(nums)-k):
        heapq.heappop(heap)
    return heapq.heappop(heap)

4825
2017-08-06 16:06


起源

什么是“lgk”? - Valentin Lorentz
@ValentinLorentz,我相信 lgx 一般意味着 log(x)。 - Dunes
我们需要更多背景。你了解时间的复杂性吗? heappush() 和 heappop()?您是否理解第4行和第5行中的循环效率低下,实际上整个例程的效率低于必要值? - Rory Daulton
事实并非如此。有一种相当简单的方法可以使用堆来表示 O() 复杂性,但这个特定的代码并不接近。 - Tim Peters
@RoryDaulton好吧,我不知道heappush()和heappop()的时间复杂度。我无法在任何地方找到它们......


答案:


heapq 是一个二进制堆,有O(log n) push 和O(log n) pop。见 heapq源代码

您显示的算法需要O(n log n)将所有项目推送到堆上,然后使用O((n-k)log n)来查找第k个最大元素。因此复杂性将是O(n log n)。它还需要O(n)额外空间。

您可以在O(n log k)中执行此操作,使用O(k)额外空间稍微修改算法。我不是Python程序员,所以你必须翻译伪代码:

create a new min-heap
push the first k nums onto the heap
for the rest of the nums:
    if num > heap.peek()
        heap.pop()
        heap.push(num)

// at this point, the k largest items are on the heap.
// The kth largest is the root:

return heap.pop()

这里的关键是堆只包含到目前为止看到的最大项目。如果一个项目小于目前为止看到的第k个最大项目,那么它永远不会被放到堆上。最坏的情况是O(n log k)。

其实, heapq 有一个 heapreplace 方法,所以你可以替换这个:

    if num > heap.peek()
        heap.pop()
        heap.push(num)

    if num > heap.peek()
        heap.replace(num)

另外,推动第一个的替代方案 k items是创建第一个列表 k 物品和电话 heapify。更优化(但仍然是O(n log k))算法是:

create array of first `k` items
heap = heapify(array)
for remaining nums
    if (num > heap.peek())
        heap.replace(num)
return heap.pop()

你也可以打电话 heapify 在整个阵列上,然后弹出第一个 n-k 项目,然后采取顶部:

heapify(nums)
for i = 0 to n-k
    heapq.heappop(nums)
return heapq.heappop(nums)

那更简单。不确定它是否比我之前的建议更快,但它修改了原始数组。复杂性是O(n)构建堆,然后O((n-k)log n)为pops。所以它是O((n-k)log n)。最坏情况O(n log n)。


12
2017-08-08 15:29



我刚刚回到这里,因为我记得发错了。我对此进行了测试,并且heapify更快(在同一输入上需要80%的时间)。但是使用直接索引进入排序(thelist)要快得多。 - Kenny Ostrom
@KennyOstrom:毫不奇怪,最后一个选项是最快的。如果OP可以修改原始数组,那么这就是他可能应该使用的那个。 - Jim Mischel
对于所有测量,我使用了制作阵列的单独副本的版本。例如heap = nums [:]; heapify(堆) - Kenny Ostrom
为什么最后一个解决方案没有 O(n + (n-k) log n)?为什么不包括 O(n) 从heapify? - user2361174
@ user2361174:因为'(n-k)log n'项在一般情况下会使O(n)项相形见绌。 - Jim Mischel