web-dev-qa-db-ja.com

heapqライブラリの関数の時間の複雑さは何ですか

私の質問は以下のリートコードの解決策からです、なぜそれが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)
25
user6617337

heapqはバイナリヒープであり、O(log n)PushおよびO(log n)popが含まれます。 heapq source code を参照してください。

表示するアルゴリズムでは、O(n log n)を使用してすべてのアイテムをヒープにプッシュし、次にO((n-k) log n)を使用してk番目に大きい要素を見つけます。したがって、複雑さはO(n log n)である必要があります。また、O(n)追加のスペースが必要です。

O(k)アルゴリズムを少し変更して余分なスペースを使用して、O(n log 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アイテムをプッシュする代わりに、最初の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)の場合)です。したがって、O((n-k) log n)。最悪の場合O(n log n)。

37
Jim Mischel