Zephyrnet-Logo

Leitfaden zu Heaps in Python

Datum:

Einleitung

Stellen Sie sich einen geschäftigen Flughafen vor, auf dem jede Minute Flüge starten und landen. So wie Fluglotsen Flüge nach Dringlichkeit priorisieren, helfen uns Heaps dabei, Daten nach bestimmten Kriterien zu verwalten und zu verarbeiten und sicherzustellen, dass die „dringendsten“ oder „wichtigsten“ Daten immer ganz oben verfügbar sind.

In diesem Leitfaden begeben wir uns auf eine Reise, um Heaps von Grund auf zu verstehen. Wir beginnen damit, zu entmystifizieren, was Heaps sind und welche inhärenten Eigenschaften sie haben. Von dort aus werden wir uns mit Pythons eigener Implementierung von Heaps befassen, dem heapq Modul und erkunden Sie die zahlreichen Funktionen. Wenn Sie sich also jemals gefragt haben, wie Sie einen dynamischen Datensatz, bei dem das Element mit der höchsten (oder niedrigsten) Priorität häufig benötigt wird, effizient verwalten können, werden Sie hier fündig.

Was ist ein Heap?

Das Erste, was Sie verstehen sollten, bevor Sie sich mit der Verwendung von Heaps befassen, ist Was ist ein Haufen?. Ein Heap zeichnet sich in der Welt der Datenstrukturen als baumbasiertes Kraftpaket aus, in dem er sich besonders gut auskennt Aufrechterhaltung von Ordnung und Hierarchie. Auch wenn es für das ungeübte Auge einem Binärbaum ähneln mag, heben es sich durch die Nuancen in seiner Struktur und die geltenden Regeln deutlich von anderen ab.

Eines der bestimmenden Merkmale eines Heaps ist seine Natur als vollständiger Binärbaum. Das bedeutet, dass jede Ebene des Baums, außer vielleicht der letzten, vollständig gefüllt ist. Innerhalb dieser letzten Ebene werden die Knoten von links nach rechts bevölkert. Eine solche Struktur stellt sicher, dass Heaps mithilfe von Arrays oder Listen effizient dargestellt und manipuliert werden können, wobei die Position jedes Elements im Array seine Platzierung im Baum widerspiegelt.

guide-to-heaps-in-python-01.png

Das wahre Wesen eines Haufens liegt jedoch in ihm Bestellung. In einer max Haufen, übersteigt der Wert eines bestimmten Knotens die Werte seiner untergeordneten Knoten oder entspricht ihnen, wodurch das größte Element direkt an der Wurzel positioniert wird. Andererseits a min haufen funktioniert nach dem umgekehrten Prinzip: Der Wert jedes Knotens ist entweder kleiner oder gleich den Werten seiner untergeordneten Knoten, wodurch sichergestellt wird, dass das kleinste Element an der Wurzel sitzt.

guide-to-heaps-in-python-02.png

Hinweis: Sie können sich einen Heap als vorstellen Zahlenpyramide. Bei einem maximalen Heap steigen die Zahlen, wenn man von der Basis zum Gipfel aufsteigt, und erreichen ihren Höhepunkt im Maximalwert am Gipfel. Im Gegensatz dazu beginnt ein Min-Heap mit dem Minimalwert an seinem Höhepunkt, wobei die Zahlen mit zunehmender Abwärtsbewegung ansteigen.

Im weiteren Verlauf werden wir tiefer darauf eingehen, wie diese inhärenten Eigenschaften von Heaps effiziente Vorgänge ermöglichen und wie Python funktioniert heapq Das Modul integriert Heaps nahtlos in unsere Codierungsbemühungen.

Merkmale und Eigenschaften von Heaps

Heaps mit ihrer einzigartigen Struktur und ihren Ordnungsprinzipien bringen eine Reihe unterschiedlicher Merkmale und Eigenschaften mit sich, die sie in verschiedenen Rechenszenarien von unschätzbarem Wert machen.

In erster Linie sind es Haufen von Natur aus effizient. Ihre baumbasierte Struktur, insbesondere das vollständige Binärbaumformat, stellt sicher, dass Vorgänge wie das Einfügen und Extrahieren von Prioritätselementen (Maximum oder Minimum) typischerweise in logarithmischer Zeit ausgeführt werden können O (log n). Diese Effizienz ist ein Segen für Algorithmen und Anwendungen, die häufigen Zugriff auf Prioritätselemente erfordern.

Eine weitere bemerkenswerte Eigenschaft von Heaps ist ihre Speichereffizienz. Da Heaps mithilfe von Arrays oder Listen dargestellt werden können, ohne dass explizite Zeiger auf untergeordnete oder übergeordnete Knoten erforderlich sind, sind sie platzsparend. Die Position jedes Elements im Array entspricht seiner Platzierung im Baum, was eine vorhersehbare und unkomplizierte Durchquerung und Manipulation ermöglicht.

Die Ordnungseigenschaft von Heaps, sei es als Max-Heap oder als Min-Heap, stellt dies sicher Die Wurzel enthält immer das Element mit der höchsten Priorität. Diese konsistente Reihenfolge ermöglicht einen schnellen Zugriff auf das Element mit der höchsten Priorität, ohne die gesamte Struktur durchsuchen zu müssen.

Darüber hinaus sind Haufen vielseitig. Während binäre Heaps (bei denen jedes übergeordnete Element höchstens zwei untergeordnete Elemente hat) am häufigsten vorkommen, können Heaps so verallgemeinert werden, dass sie mehr als zwei untergeordnete Elemente haben, was als bezeichnet wird d-ary haufen. Diese Flexibilität ermöglicht eine Feinabstimmung basierend auf spezifischen Anwendungsfällen und Leistungsanforderungen.

Schließlich sind Haufen selbstjustierend. Immer wenn Elemente hinzugefügt oder entfernt werden, ordnet sich die Struktur neu an, um ihre Eigenschaften beizubehalten. Durch diesen dynamischen Ausgleich wird sichergestellt, dass der Heap jederzeit für seine Kernoperationen optimiert bleibt.

Hinweis: Aufgrund dieser Eigenschaften eignete sich die Heap-Datenstruktur gut für einen effizienten Sortieralgorithmus – die Heap-Sortierung. Um mehr über die Heap-Sortierung in Python zu erfahren, lesen Sie unsere „Heap-Sortierung in Python“ Artikel.

Wenn wir tiefer in die Implementierung und die praktischen Anwendungen von Python eintauchen, wird sich das wahre Potenzial von Heaps vor uns entfalten.

Arten von Haufen

Nicht alle Heaps sind gleich. Abhängig von ihrer Reihenfolge und ihren strukturellen Eigenschaften können Heaps in verschiedene Typen eingeteilt werden, von denen jeder seine eigenen Anwendungen und Vorteile hat. Die beiden Hauptkategorien sind max Haufen und min haufen.

Das charakteristischste Merkmal von a max Haufen bedeutet, dass der Wert eines bestimmten Knotens größer oder gleich den Werten seiner untergeordneten Knoten ist. Dadurch wird sichergestellt, dass sich das größte Element im Heap immer im Stammverzeichnis befindet. Eine solche Struktur ist besonders nützlich, wenn häufig auf das maximale Element zugegriffen werden muss, wie z. B. bei bestimmten Implementierungen von Prioritätswarteschlangen.

Das Gegenstück zum Max-Heap, a min haufen stellt sicher, dass der Wert eines bestimmten Knotens kleiner oder gleich den Werten seiner untergeordneten Knoten ist. Dadurch wird das kleinste Element des Heaps an der Wurzel positioniert. Min-Heaps sind in Szenarien von unschätzbarem Wert, in denen das kleinste Element von größter Bedeutung ist, beispielsweise in Algorithmen, die sich mit der Echtzeit-Datenverarbeitung befassen.

Über diese Hauptkategorien hinaus können Heaps auch anhand ihres Verzweigungsfaktors unterschieden werden:

Während binäre Heaps am häufigsten vorkommen und jeder Elternteil höchstens zwei Kinder hat, kann das Konzept der Heaps auf Knoten mit mehr als zwei Kindern erweitert werden. In einem d-ary Haufen, jeder Knoten hat höchstens d Kinder. Diese Variante kann für bestimmte Szenarien optimiert werden, z. B. durch Verringern der Baumhöhe, um bestimmte Vorgänge zu beschleunigen.

Binomialhaufen ist eine Menge von Binomialbäumen, die rekursiv definiert werden. Binomial-Heaps werden in Implementierungen von Prioritätswarteschlangen verwendet und bieten effiziente Zusammenführungsvorgänge.

Benannt nach der berühmten Fibonacci-Folge Fibonacci-Haufen bietet im Vergleich zu binären oder binomialen Heaps für viele Vorgänge amortisiertere Laufzeiten. Sie sind besonders nützlich in Netzwerkoptimierungsalgorithmen.

Pythons Heap-Implementierung – Die Heapq Modul

Python bietet ein integriertes Modul für Heap-Operationen – das heapq Modul. Dieses Modul bietet eine Sammlung von Heap-bezogenen Funktionen, die es Entwicklern ermöglichen, Listen in Heaps umzuwandeln und verschiedene Heap-Operationen durchzuführen, ohne dass eine benutzerdefinierte Implementierung erforderlich ist. Lassen Sie uns in die Nuancen dieses Moduls eintauchen und wie es Ihnen die Leistungsfähigkeit von Heaps bietet.

Das heapq Das Modul stellt keinen eindeutigen Heap-Datentyp bereit. Stattdessen bietet es Funktionen, die mit regulären Python-Listen arbeiten, diese umwandeln und behandeln binäre Haufen.

Dieser Ansatz ist sowohl speichereffizient als auch nahtlos in die vorhandenen Datenstrukturen von Python integriert.

Das bedeutet, dass Heaps werden als Listen dargestellt in heapq. Das Schöne an dieser Darstellung ist ihre Einfachheit – das nullbasierte Listenindexsystem dient als impliziter Binärbaum. Für jedes gegebene Element an der Position i, es ist:

  • Das linke Kind ist an der richtigen Stelle 2*i + 1
  • Das rechte Kind ist in Position 2*i + 2
  • Der übergeordnete Knoten befindet sich an der Position (i-1)//2

guide-to-heaps-in-python-03.png

Diese implizite Struktur stellt sicher, dass keine separate knotenbasierte Binärbaumdarstellung erforderlich ist, was die Vorgänge unkompliziert und den Speicherverbrauch minimal macht.

Raumkomplexität: Heaps werden normalerweise als Binärbäume implementiert, erfordern jedoch keine Speicherung expliziter Zeiger für untergeordnete Knoten. Dadurch sind sie platzsparend bei einer Platzkomplexität von O (n) zum Speichern von n Elementen.

Es ist wichtig zu beachten, dass die heapq Modulen Erstellt standardmäßig minimale Heaps. Das bedeutet, dass das kleinste Element immer an der Wurzel (bzw. an der ersten Position in der Liste) steht. Wenn Sie einen maximalen Heap benötigen, müssen Sie die Reihenfolge umkehren, indem Sie die Elemente mit multiplizieren -1 oder verwenden Sie eine benutzerdefinierte Vergleichsfunktion.

Pythons heapq Das Modul bietet eine Reihe von Funktionen, mit denen Entwickler verschiedene Heap-Operationen für Listen ausführen können.

Hinweis: So verwenden Sie die heapq Wenn Sie das Modul in Ihrer Anwendung verwenden möchten, müssen Sie es mit Simple importieren import heapq.

In den folgenden Abschnitten werden wir uns eingehend mit jeder dieser grundlegenden Operationen befassen und ihre Mechanismen und Anwendungsfälle untersuchen.

So wandeln Sie eine Liste in einen Heap um

Das heapify() Die Funktion ist der Ausgangspunkt für viele Heap-bezogene Aufgaben. Es nimmt eine Iterable (normalerweise eine Liste) und ordnet ihre Elemente direkt an Ort und Stelle neu an, um die Eigenschaften eines Min-Heaps zu erfüllen:

Sehen Sie sich unseren praxisnahen, praktischen Leitfaden zum Erlernen von Git an, mit Best Practices, branchenweit akzeptierten Standards und einem mitgelieferten Spickzettel. Hören Sie auf, Git-Befehle zu googeln und tatsächlich in Verbindung, um es!

import heapq data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
heapq.heapify(data)
print(data)

Dadurch wird eine neu geordnete Liste ausgegeben, die einen gültigen Mindestheap darstellt:

[1, 1, 2, 3, 3, 9, 4, 6, 5, 5, 5]

Zeitliche Komplexität: Konvertieren einer ungeordneten Liste in einen Heap mithilfe von heapify Funktion ist eine O (n) Betrieb. Das erscheint vielleicht kontraintuitiv, wie man es vielleicht erwarten würde O (nlogn), kann aber aufgrund der Eigenschaften der Baumstruktur in linearer Zeit erreicht werden.

So fügen Sie dem Heap ein Element hinzu

Das heappush() Mit der Funktion können Sie ein neues Element in den Heap einfügen und dabei die Eigenschaften des Heaps beibehalten:

import heapq heap = []
heapq.heappush(heap, 5)
heapq.heappush(heap, 3)
heapq.heappush(heap, 7)
print(heap)

Wenn Sie den Code ausführen, erhalten Sie eine Liste der Elemente, die die Min-Heap-Eigenschaft beibehalten:

[3, 5, 7]

Zeitliche Komplexität: Der Einfügungsvorgang in einen Heap, bei dem ein neues Element im Heap unter Beibehaltung der Heap-Eigenschaft platziert wird, hat eine zeitliche Komplexität von O (logn). Dies liegt daran, dass das Element im schlimmsten Fall möglicherweise vom Blatt zur Wurzel wandern muss.

So entfernen Sie das kleinste Element aus dem Heap und geben es zurück

Das heappop() Die Funktion extrahiert das kleinste Element aus dem Heap (die Wurzel in einem Min-Heap) und gibt es zurück. Nach dem Entfernen wird sichergestellt, dass die Liste ein gültiger Heap bleibt:

import heapq heap = [1, 3, 5, 7, 9]
print(heapq.heappop(heap))
print(heap)

Hinweis: Das heappop() ist von unschätzbarem Wert bei Algorithmen, die Verarbeitungselemente in aufsteigender Reihenfolge erfordern, wie etwa dem Heap-Sort-Algorithmus, oder bei der Implementierung von Prioritätswarteschlangen, in denen Aufgaben basierend auf ihrer Dringlichkeit ausgeführt werden.

Dadurch werden das kleinste Element und die verbleibende Liste ausgegeben:

1
[3, 7, 5, 9]

Hier 1 ist das kleinste Element aus der heap, und die verbleibende Liste hat die Heap-Eigenschaft beibehalten, auch nachdem wir sie entfernt haben 1.

Zeitliche Komplexität: Das Entfernen des Stammelements (das das kleinste in einem Min-Heap oder das größte in einem Max-Heap ist) und das Neuorganisieren des Heaps dauert ebenfalls O (logn) Zeit.

So schieben Sie einen neuen Gegenstand und platzen den kleinsten Gegenstand

Das heappushpop() Die Funktion ist eine kombinierte Operation, die ein neues Element auf den Heap schiebt und dann das kleinste Element aus dem Heap entnimmt und zurückgibt:

import heapq heap = [3, 5, 7, 9]
print(heapq.heappushpop(heap, 4)) print(heap)

Dies wird ausgegeben 3, das kleinste Element, und drucken Sie das neue aus heap Liste, die jetzt enthält 4 unter Beibehaltung der Heap-Eigenschaft:

3
[4, 5, 7, 9]

Hinweis: Verwendung der heappushpop() Die Funktion ist effizienter als die Durchführung von Vorgängen zum Schieben eines neuen Elements und zum separaten Öffnen des kleinsten Elements.

So ersetzen Sie den kleinsten Artikel und verschieben einen neuen Artikel

Das heapreplace() Funktion öffnet das kleinste Element und schiebt ein neues Element auf den Heap, alles in einem effizienten Vorgang:

import heapq heap = [1, 5, 7, 9]
print(heapq.heapreplace(heap, 4))
print(heap)

Dies wird gedruckt 1, das kleinste Element, und die Liste enthält jetzt 4 und behält die Heap-Eigenschaft bei:

1
[4, 5, 7, 9]

Note: heapreplace() ist in Streaming-Szenarien von Vorteil, in denen Sie das aktuell kleinste Element durch einen neuen Wert ersetzen möchten, z. B. bei rollierenden Fenstervorgängen oder Echtzeit-Datenverarbeitungsaufgaben.

Mehrere Extreme in Pythons Heap finden

nlargest(n, iterable[, key]) und nsmallest(n, iterable[, key]) Funktionen dienen dazu, mehrere größte oder kleinste Elemente aus einem iterierbaren Element abzurufen. Sie können effizienter sein als das Sortieren des gesamten Iterables, wenn Sie nur einige Extremwerte benötigen. Angenommen, Sie haben die folgende Liste und möchten drei kleinste und drei größte Werte in der Liste finden:

data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]

Hier nlargest() und nsmallest() Funktionen können nützlich sein:

import heapq data = [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5]
print(heapq.nlargest(3, data)) print(heapq.nsmallest(3, data)) 

Dadurch erhalten Sie zwei Listen – eine enthält die drei größten Werte und die andere die drei kleinsten Werte aus data Liste:

[9, 6, 5]
[1, 1, 2]

So erstellen Sie Ihren benutzerdefinierten Heap

Während Pythons heapq Obwohl das Modul einen robusten Satz an Tools für die Arbeit mit Heaps bereitstellt, gibt es Szenarien, in denen das standardmäßige Min-Heap-Verhalten möglicherweise nicht ausreicht. Unabhängig davon, ob Sie einen maximalen Heap implementieren möchten oder einen Heap benötigen, der auf benutzerdefinierten Vergleichsfunktionen basiert, kann die Erstellung eines benutzerdefinierten Heaps die Antwort sein. Lassen Sie uns untersuchen, wie Sie Heaps an bestimmte Anforderungen anpassen können.

Implementieren eines Max Heap mit heapq

Standardmäßig heapq schafft Min. Haufen. Mit einem einfachen Trick können Sie jedoch einen maximalen Heap implementieren. Die Idee besteht darin, die Reihenfolge der Elemente umzukehren, indem man sie mit multipliziert -1 bevor Sie sie zum Heap hinzufügen:

import heapq class MaxHeap: def __init__(self): self.heap = [] def push(self, val): heapq.heappush(self.heap, -val) def pop(self): return -heapq.heappop(self.heap) def peek(self): return -self.heap[0]

Bei diesem Ansatz wird die größte Zahl (bezogen auf den absoluten Wert) zur kleinsten, was die heapq Funktionen zur Aufrechterhaltung einer maximalen Heap-Struktur.

Heaps mit benutzerdefinierten Vergleichsfunktionen

Manchmal benötigen Sie möglicherweise einen Heap, der nicht nur anhand der natürlichen Reihenfolge der Elemente vergleicht. Wenn Sie beispielsweise mit komplexen Objekten arbeiten oder bestimmte Sortierkriterien haben, ist eine benutzerdefinierte Vergleichsfunktion unerlässlich.

Um dies zu erreichen, können Sie Elemente in eine Hilfsklasse einschließen, die die Vergleichsoperatoren überschreibt:

import heapq class CustomElement: def __init__(self, obj, comparator): self.obj = obj self.comparator = comparator def __lt__(self, other): return self.comparator(self.obj, other.obj) def custom_heappush(heap, obj, comparator=lambda x, y: x < y): heapq.heappush(heap, CustomElement(obj, comparator)) def custom_heappop(heap): return heapq.heappop(heap).obj

Mit diesem Setup können Sie jede benutzerdefinierte Komparatorfunktion definieren und mit dem Heap verwenden.

Zusammenfassung

Heaps bieten eine vorhersehbare Leistung für viele Vorgänge und sind daher eine zuverlässige Wahl für prioritätsbasierte Aufgaben. Es ist jedoch wichtig, die spezifischen Anforderungen und Eigenschaften der jeweiligen Anwendung zu berücksichtigen. In einigen Fällen kann eine Optimierung der Heap-Implementierung oder sogar die Entscheidung für alternative Datenstrukturen zu einer besseren Leistung in der Praxis führen.

Wie wir gesehen haben, sind Heaps mehr als nur eine weitere Datenstruktur. Sie stellen ein Zusammenspiel von Effizienz, Struktur und Anpassungsfähigkeit dar. Von ihren grundlegenden Eigenschaften bis zu ihrer Implementierung in Python heapq Modul bieten Heaps eine robuste Lösung für eine Vielzahl von Rechenherausforderungen, insbesondere solche, bei denen es um Priorität geht.

spot_img

Neueste Intelligenz

spot_img