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