web-dev-qa-db-fra.com

Quelle est la complexité temporelle des fonctions dans la bibliothèque heapq

Ma question provient de la solution dans leetetcode ci-dessous, je ne comprends pas pourquoi c'est O(k+(n-k)log(k)).

Supplément: Peut-être que la complexité n'est pas ça, en fait je ne connais pas la complexité temporelle de heappush() et 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 est un tas binaire, avec O (log n) Push et O (log n) pop. Voir code source heapq .

L'algorithme que vous montrez prend O (n log n) pour pousser tous les éléments sur le tas, puis O((n-k) log n) pour trouver le kème plus grand élément. La complexité serait donc être O (n log n). Il nécessite également O(n) espace supplémentaire.

Vous pouvez le faire dans O (n log k), en utilisant O(k) espace supplémentaire en modifiant légèrement l'algorithme. Je ne suis pas un programmeur Python , vous devrez donc traduire le pseudocode:

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()

La clé ici est que le tas contient uniquement les plus gros éléments vus jusqu'à présent. Si un élément est plus petit que le kième plus grand vu jusqu'à présent, il n'est jamais mis sur le tas. Le pire des cas est O (n log k).

En fait, heapq a une méthode heapreplace, vous pouvez donc remplacer ceci:

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

avec

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

De plus, une alternative à l'envoi des premiers k éléments est de créer une liste des premiers k éléments et d'appeler heapify. Un algorithme plus optimisé (mais toujours O (n log k)) est:

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

Vous pouvez également appeler heapify sur l'ensemble du tableau, puis afficher le premier n-k éléments, puis prenez le dessus:

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

C'est plus simple. Je ne sais pas si c'est plus rapide que ma suggestion précédente, mais cela modifie le tableau d'origine. La complexité est O(n) pour construire le tas, puis O((n-k) log n) pour les pops. Donc ce sera O((n-k) log n). Pire cas O (n log n).

37
Jim Mischel