Gids voor Heaps in Python

Gids voor Heaps in Python

Introductie

Stel je een bruisende luchthaven voor waar elke minuut vluchten vertrekken en landen. Net zoals luchtverkeersleiders vluchten prioriteren op basis van urgentie, helpen heaps ons gegevens te beheren en te verwerken op basis van specifieke criteria, waardoor ervoor wordt gezorgd dat het meest โ€œurgenteโ€ of โ€œbelangrijkeโ€ stukje data altijd bovenaan toegankelijk is.

In deze gids gaan we op reis om hopen van de grond af te begrijpen. We beginnen met het ontrafelen van wat hopen zijn en wat hun inherente eigenschappen zijn. Van daaruit duiken we in Python's eigen implementatie van heaps, de heapq module en verken de rijke reeks functionaliteiten. Dus als u zich ooit heeft afgevraagd hoe u efficiรซnt een dynamische set gegevens kunt beheren waarbij het element met de hoogste (of laagste) prioriteit vaak nodig is, staat u iets lekkers te wachten.

Wat is een hoop?

Het eerste dat je moet begrijpen voordat je je gaat verdiepen in het gebruik van hopen is wat is een hoop. A heap onderscheidt zich in de wereld van datastructuren als een op bomen gebaseerde krachtpatser, waar hij bijzonder bedreven in is het handhaven van orde en hiรซrarchie. Hoewel het voor het ongetrainde oog misschien op een binaire boom lijkt, onderscheiden de nuances in de structuur en de bestuursregels het duidelijk.

Een van de bepalende kenmerken van een hoop is de aard ervan als een complete binaire boom. Dit betekent dat elk niveau van de boom, behalve misschien het laatste, volledig gevuld is. Binnen dit laatste niveau vullen de knooppunten zich van links naar rechts. Een dergelijke structuur zorgt ervoor dat heaps efficiรซnt kunnen worden weergegeven en gemanipuleerd met behulp van arrays of lijsten, waarbij de positie van elk element in de array de plaatsing in de boom weerspiegelt.

gids-naar-heaps-in-python-01.png

De ware essentie van een hoop ligt echter in de inhoud ervan bestellen. In een maximale hoop, overtreft de waarde van een bepaald knooppunt de waarden van zijn onderliggende knooppunten of is deze gelijk, waarbij het grootste element precies bij de wortel wordt geplaatst. Aan de andere kant: een min hoop werkt volgens het tegenovergestelde principe: de waarde van elk knooppunt is kleiner dan of gelijk aan de waarden van zijn onderliggende knooppunten, waardoor wordt gegarandeerd dat het kleinste element in de wortel zit.

gids-naar-heaps-in-python-02.png

Advies: Je kunt een hoop visualiseren als een piramide van getallen. Voor een maximale hoop nemen de aantallen toe naarmate je van de basis naar de top stijgt, met als hoogtepunt de maximale waarde op het hoogtepunt. Daarentegen begint een min-heap met de minimumwaarde op zijn hoogtepunt, waarbij de getallen escaleren naarmate je naar beneden beweegt.

Naarmate we verder komen, zullen we dieper ingaan op hoe deze inherente eigenschappen van heaps efficiรซnte operaties mogelijk maken en hoe Python dat doet heapq module integreert naadloos veel in onze codeerinspanningen.

Kenmerken en eigenschappen van hopen

Heaps, met hun unieke structuur en ordeningsprincipes, brengen een reeks onderscheidende kenmerken en eigenschappen voort die ze van onschatbare waarde maken in verschillende computerscenario's.

In de eerste plaats zijn er hopen inherent efficiรซnt. Hun op bomen gebaseerde structuur, met name het volledige binaire boomformaat, zorgt ervoor dat bewerkingen zoals het invoegen en extraheren van prioriteitselementen (maximum of minimum) doorgaans in logaritmische tijd kunnen worden uitgevoerd. O (log n). Deze efficiรซntie is een zegen voor algoritmen en applicaties die frequente toegang tot prioriteitselementen vereisen.

Een andere opmerkelijke eigenschap van hopen is hun geheugen efficiรซntie. Omdat heaps kunnen worden weergegeven met behulp van arrays of lijsten zonder dat er expliciete verwijzingen naar onderliggende of bovenliggende knooppunten nodig zijn, zijn ze ruimtebesparend. De positie van elk element in de array komt overeen met de plaatsing ervan in de boom, waardoor voorspelbare en eenvoudige verplaatsing en manipulatie mogelijk is.

De ordeningseigenschap van heaps, of het nu een max heap of een min heap is, zorgt daarvoor de wortel bevat altijd het element met de hoogste prioriteit. Deze consistente volgorde zorgt voor snelle toegang tot het element met de hoogste prioriteit zonder dat u door de hele structuur hoeft te zoeken.

Bovendien zijn er hopen veelzijdig. Hoewel binaire heaps (waarbij elke ouder maximaal twee kinderen heeft) het meest voorkomen, kunnen heaps worden gegeneraliseerd om meer dan twee kinderen te hebben, ook wel bekend als d-aire hopen. Deze flexibiliteit maakt afstemming mogelijk op basis van specifieke gebruiksscenario's en prestatie-eisen.

Ten slotte zijn er hopen zelfinstellend. Telkens wanneer elementen worden toegevoegd of verwijderd, herschikt de structuur zichzelf om zijn eigenschappen te behouden. Deze dynamische balancering zorgt ervoor dat de heap te allen tijde geoptimaliseerd blijft voor zijn kernactiviteiten.

Advies: Deze eigenschappen zorgden ervoor dat de heap-datastructuur goed paste bij een efficiรซnt sorteeralgoritme: heap sort. Voor meer informatie over heap sortering in Python, lees onze โ€œHeap sorteren in Pythonโ€ artikel.

Naarmate we dieper ingaan op de implementatie en praktische toepassingen van Python, zal het ware potentieel van hopen zich voor ons ontvouwen.

Soorten hopen

Niet alle hopen zijn gelijk geschapen. Afhankelijk van hun ordening en structurele eigenschappen kunnen hopen worden onderverdeeld in verschillende typen, elk met zijn eigen reeks toepassingen en voordelen. De twee hoofdcategorieรซn zijn maximale hoop en min hoop.

Het meest onderscheidende kenmerk van a maximale hoop is dat de waarde van een bepaald knooppunt groter is dan of gelijk is aan de waarden van zijn kinderen. Dit zorgt ervoor dat het grootste element in de heap zich altijd in de root bevindt. Een dergelijke structuur is met name handig wanneer er behoefte is aan frequente toegang tot het maximale element, zoals bij bepaalde implementaties van prioriteitswachtrijen.

De tegenhanger van de maximale heap, a min hoop zorgt ervoor dat de waarde van een bepaald knooppunt kleiner is dan of gelijk is aan de waarden van zijn onderliggende knooppunten. Hierdoor wordt het kleinste element van de hoop bij de wortel geplaatst. Min-heaps zijn van onschatbare waarde in scenario's waarin het kleinste element van het allergrootste belang is, zoals in algoritmen die zich bezighouden met realtime gegevensverwerking.

Naast deze primaire categorieรซn kunnen hopen ook worden onderscheiden op basis van hun vertakkingsfactor:

Hoewel binaire heaps het meest voorkomen, waarbij elke ouder maximaal twee kinderen heeft, kan het concept van heaps worden uitgebreid tot knooppunten met meer dan twee kinderen. In een d-aire hoop, heeft elk knooppunt maximaal d kinderen. Deze variatie kan worden geoptimaliseerd voor specifieke scenario's, zoals het verkleinen van de hoogte van de boom om bepaalde handelingen te versnellen.

Binomiale hoop is een reeks binomiale bomen die recursief zijn gedefinieerd. Binomiale heaps worden gebruikt bij implementaties van prioriteitswachtrijen en bieden efficiรซnte samenvoegbewerkingen.

Vernoemd naar de beroemde Fibonacci-reeks, de Fibonacci-hoop biedt voor veel bewerkingen beter afgeschreven looptijden in vergelijking met binaire of binomiale heaps. Ze zijn vooral nuttig in algoritmen voor netwerkoptimalisatie.

Python's Heap-implementatie โ€“ The hoopje Module

Python biedt een ingebouwde module voor heap-bewerkingen โ€“ de heapq module. Deze module biedt een verzameling heap-gerelateerde functies waarmee ontwikkelaars lijsten in heaps kunnen transformeren en verschillende heap-bewerkingen kunnen uitvoeren zonder dat een aangepaste implementatie nodig is. Laten we eens kijken naar de nuances van deze module en hoe deze u de kracht van hopen biedt.

De heapq module biedt geen afzonderlijk heap-gegevenstype. In plaats daarvan biedt het functies die werken op gewone Python-lijsten, waarbij deze worden getransformeerd en behandeld als binaire hopen.

Deze aanpak is zowel geheugenefficiรซnt als naadloos te integreren met de bestaande datastructuren van Python.

Dat betekent dat heaps worden weergegeven als lijsten in heapq. Het mooie van deze representatie is de eenvoud ervan: het op nul gebaseerde lijstindexsysteem fungeert als een impliciete binaire boom. Voor elk gegeven element op positie i, zijn:

  • Linkerkind bevindt zich op positie 2*i + 1
  • Het rechterkind is op positie 2*i + 2
  • Parent Node bevindt zich op positie (i-1)//2

gids-naar-heaps-in-python-03.png

Deze impliciete structuur zorgt ervoor dat er geen behoefte is aan een aparte op knooppunten gebaseerde binaire boomrepresentatie, waardoor bewerkingen eenvoudig zijn en het geheugengebruik minimaal.

Ruimtecomplexiteit: Heaps worden doorgaans geรฏmplementeerd als binaire bomen, maar vereisen geen opslag van expliciete verwijzingen voor onderliggende knooppunten. Dit maakt ze ruimte-efficiรซnt met een ruimtecomplexiteit van O (n) voor het opslaan van n elementen.

Het is essentieel op te merken dat de heapq module maakt standaard min-heaps aan. Dit betekent dat het kleinste element altijd in de root (of op de eerste positie in de lijst) staat. Als je een maximale heap nodig hebt, moet je de volgorde omkeren door elementen te vermenigvuldigen -1 of gebruik een aangepaste vergelijkingsfunctie.

Python's heapq module biedt een reeks functies waarmee ontwikkelaars verschillende heap-bewerkingen op lijsten kunnen uitvoeren.

Opmerking: Om het gebruik heapq module in uw toepassing, moet u deze importeren met simple import heapq.

In de volgende secties duiken we diep in elk van deze fundamentele bewerkingen, waarbij we hun werking en gebruiksscenario's onderzoeken.

Hoe je een lijst in een hoop kunt transformeren

De heapify() functie is het startpunt voor veel heap-gerelateerde taken. Er is een iterabele (meestal een lijst) voor nodig en de elementen worden op hun plaats herschikt om aan de eigenschappen van een min-heap te voldoen:

Bekijk onze praktische, praktische gids voor het leren van Git, met best-practices, door de industrie geaccepteerde normen en bijgevoegd spiekbriefje. Stop met Googlen op Git-commando's en eigenlijk leren het!

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

Hierdoor wordt een opnieuw geordende lijst uitgevoerd die een geldige minimale heap vertegenwoordigt:

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

Tijdscomplexiteit: Een ongeordende lijst omzetten in een heap met behulp van de heapify functie is een O (n) operatie. Dit lijkt misschien contra-intuรฏtief, zoals je zou verwachten O (nlogn), maar vanwege de eigenschappen van de boomstructuur kan dit in lineaire tijd worden bereikt.

Hoe u een element aan de heap toevoegt

De heappush() Met de functie kunt u een nieuw element in de heap invoegen terwijl de eigenschappen van de heap behouden blijven:

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

Als u de code uitvoert, krijgt u een lijst met elementen die de eigenschap min heap behouden:

[3, 5, 7]

Tijdscomplexiteit: De invoegoperatie in een heap, waarbij een nieuw element in de heap wordt geplaatst terwijl de heap-eigenschap behouden blijft, heeft een tijdscomplexiteit van O (ingelogd). Dit komt omdat in het ergste geval het element mogelijk van het blad naar de wortel moet reizen.

Hoe het kleinste element uit de hoop te verwijderen en terug te plaatsen

De heappop() functie extraheert en retourneert het kleinste element uit de heap (de wortel in een min-heap). Na verwijdering zorgt het ervoor dat de lijst een geldige hoop blijft:

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

Opmerking: De heappop() is van onschatbare waarde bij algoritmen die verwerkingselementen in oplopende volgorde vereisen, zoals het Heap Sort-algoritme, of bij het implementeren van prioriteitswachtrijen waarbij taken worden uitgevoerd op basis van hun urgentie.

Dit levert het kleinste element en de resterende lijst op:

1
[3, 7, 5, 9]

Hier 1 is het kleinste element uit de heap, en de resterende lijst heeft de heap-eigenschap behouden, zelfs nadat we deze hadden verwijderd 1.

Tijdscomplexiteit: Het verwijderen van het rootelement (dat het kleinste is in een min-heap of het grootste in een max-heap) en het reorganiseren van de heap duurt ook O (ingelogd) tijd.

Hoe je een nieuw item kunt pushen en het kleinste item kunt laten knallen

De heappushpop() functie is een gecombineerde bewerking die een nieuw item op de hoop duwt en vervolgens het kleinste item uit de hoop laat springen:

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

Dit wordt uitgevoerd 3, het kleinste element, en print het nieuwe uit heap lijst die nu bestaat 4 met behoud van de heap-eigenschap:

3
[4, 5, 7, 9]

Opmerking: De heappushpop() Deze functie is efficiรซnter dan het uitvoeren van bewerkingen waarbij een nieuw element wordt geduwd en het kleinste afzonderlijk wordt losgelaten.

Hoe je het kleinste item vervangt en een nieuw item pusht

De heapreplace() functie haalt het kleinste element eruit en duwt een nieuw element op de hoop, alles in รฉรฉn efficiรซnte handeling:

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

Dit wordt afgedrukt 1, het kleinste element, en de lijst bevat nu 4 en behoudt de heap-eigenschap:

1
[4, 5, 7, 9]

Note: heapreplace() is nuttig in streamingscenario's waarin u het huidige kleinste element wilt vervangen door een nieuwe waarde, zoals bij rolling window-bewerkingen of realtime gegevensverwerkingstaken.

Meerdere uitersten vinden in Python's Heap

nlargest(n, iterable[, key]) en nsmallest(n, iterable[, key]) functies zijn ontworpen om meerdere grootste of kleinste elementen uit een iterabele op te halen. Ze kunnen efficiรซnter zijn dan het sorteren van de hele iterabele als je slechts een paar extreme waarden nodig hebt. Stel dat u bijvoorbeeld de volgende lijst heeft en dat u de drie kleinste en de drie grootste waarden in de lijst wilt vinden:

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

Hier nlargest() en nsmallest() functies kunnen van pas komen:

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

Dit geeft je twee lijsten: de ene bevat de drie grootste waarden en de andere bevat de drie kleinste waarden uit de data lijst:

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

Hoe u uw aangepaste heap kunt bouwen

Terwijl Python's heapq -module biedt een robuuste set tools voor het werken met heaps. Er zijn scenario's waarin het standaard min-heap-gedrag mogelijk niet voldoende is. Of u nu een max heap wilt implementeren of een heap nodig hebt die werkt op basis van aangepaste vergelijkingsfuncties, het bouwen van een aangepaste heap kan de oplossing zijn. Laten we eens kijken hoe we hopen kunnen afstemmen op specifieke behoeften.

Een Max Heap implementeren met behulp van heapq

Standaard heapq creรซert min hopen. Met een eenvoudige truc kun je het echter gebruiken om een โ€‹โ€‹maximale heap te implementeren. Het idee is om de volgorde van de elementen om te keren door ze te vermenigvuldigen -1 voordat je ze aan de heap toevoegt:

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]

Met deze aanpak wordt het grootste getal (in termen van absolute waarde) het kleinste, waardoor de heapq functies om een โ€‹โ€‹maximale heap-structuur te behouden.

Enorm veel met aangepaste vergelijkingsfuncties

Soms heb je misschien een hoop nodig die niet alleen kan worden vergeleken op basis van de natuurlijke volgorde van de elementen. Als u bijvoorbeeld met complexe objecten werkt of specifieke sorteercriteria heeft, wordt een aangepaste vergelijkingsfunctie essentieel.

Om dit te bereiken, kunt u elementen in een helperklasse plaatsen die de vergelijkingsoperatoren overschrijft:

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

Met deze opstelling kunt u elke aangepaste comparatorfunctie definiรซren en deze met de heap gebruiken.

Conclusie

Heaps bieden voorspelbare prestaties voor veel bewerkingen, waardoor ze een betrouwbare keuze zijn voor op prioriteit gebaseerde taken. Het is echter essentieel om rekening te houden met de specifieke vereisten en kenmerken van de toepassing in kwestie. In sommige gevallen kan het aanpassen van de heap-implementatie of zelfs het kiezen voor alternatieve datastructuren betere prestaties in de echte wereld opleveren.

Heaps, waar we doorheen zijn gereisd, zijn meer dan zomaar een datastructuur. Ze vertegenwoordigen een samenloop van efficiรซntie, structuur en aanpassingsvermogen. Van hun fundamentele eigenschappen tot hun implementatie in Python heapq module bieden heaps een robuuste oplossing voor een groot aantal computationele uitdagingen, vooral die waarbij prioriteit centraal staat.

Tijdstempel:

Meer van Stapelmisbruik