Guide til dynger i Python

Guide til dynger i Python

Introduktion

Forestil dig en travl lufthavn med fly, der letter og lander hvert minut. Ligesom flyveledere prioriterer flyvninger baseret på haster, hjælper heaps os med at administrere og behandle data baseret på specifikke kriterier, hvilket sikrer, at den mest "hastende" eller "vigtige" del af data altid er tilgængelig øverst.

I denne guide vil vi tage på en rejse for at forstå dynger fra bunden. Vi starter med at afmystificere, hvad dynger er og deres iboende egenskaber. Derfra dykker vi ned i Pythons egen implementering af heaps, den heapq modul, og udforsk dets rige sæt af funktionaliteter. Så hvis du nogensinde har undret dig over, hvordan du effektivt administrerer et dynamisk sæt af data, hvor det højest (eller laveste) prioritetselement ofte er nødvendigt, er du med på en godbid.

Hvad er en Heap?

Den første ting, du gerne vil forstå, før du dykker ned i brugen af ​​dynger, er hvad er en bunke. En bunke skiller sig ud i verden af ​​datastrukturer som et træbaseret kraftcenter, særligt dygtig til opretholde orden og hierarki. Selvom det kan ligne et binært træ for det utrænede øje, adskiller nuancerne i dets struktur og styrende regler det tydeligt.

Et af de definerende kendetegn ved en bunke er dens natur som en komplet binært træ. Det betyder, at hvert niveau i træet, måske undtagen det sidste, er helt fyldt. Inden for dette sidste niveau udfyldes noder fra venstre mod højre. En sådan struktur sikrer, at dynger effektivt kan repræsenteres og manipuleres ved hjælp af arrays eller lister, hvor hvert elements position i arrayet afspejler dets placering i træet.

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

Den sande essens af en bunke ligger imidlertid i dens bestilling. I en max bunke, en given nodes værdi overgår eller er lig med værdierne for dens børn, hvilket placerer det største element lige ved roden. På den anden side, en min bunke fungerer efter det modsatte princip: enhver nodes værdi er enten mindre end eller lig med dens børns værdier, hvilket sikrer, at det mindste element sidder ved roden.

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

Rådgivning: Du kan visualisere en bunke som en pyramide af tal. For en maksimal bunke, når du stiger fra basen til toppen, stiger tallene, og kulminerer med den maksimale værdi på toppen. I modsætning hertil starter en min heap med minimumsværdien på sit højeste, med tal, der eskalerer, når du bevæger dig nedad.

Efterhånden som vi gør fremskridt, vil vi dykke dybere ned i, hvordan disse iboende egenskaber af dynger muliggør effektive operationer, og hvordan Pythons heapq modul integrerer problemfrit dynger i vores kodningsbestræbelser.

Dyngers egenskaber og egenskaber

Dynger frembringer med deres unikke struktur og ordensprincipper et sæt forskellige karakteristika og egenskaber, der gør dem uvurderlige i forskellige beregningsscenarier.

Først og fremmest er dynger iboende effektiv. Deres træbaserede struktur, specifikt det komplette binære træformat, sikrer, at operationer som indsættelse og udtrækning af prioritetselementer (maksimum eller minimum) kan udføres i logaritmisk tid, typisk O (log n). Denne effektivitet er en velsignelse for algoritmer og applikationer, der kræver hyppig adgang til prioriterede elementer.

En anden bemærkelsesværdig egenskab ved dynger er deres hukommelseseffektivitet. Da dynger kan repræsenteres ved hjælp af arrays eller lister uden behov for eksplicitte pointere til under- eller overordnede noder, er de pladsbesparende. Hvert elements position i arrayet svarer til dets placering i træet, hvilket giver mulighed for forudsigelig og ligetil gennemgang og manipulation.

Bestillingsegenskaben for heaps, hvad enten det er en max heap eller en min heap, sikrer det roden har altid elementet med højeste prioritet. Denne konsekvente bestilling er det, der giver mulighed for hurtig adgang til topprioritetselementet uden at skulle søge gennem hele strukturen.

Ydermere er dynger alsidige. Mens binære heaps (hvor hver forælder højst har to børn) er de mest almindelige, kan heaps generaliseres til at have mere end to børn, kendt som d-ary dynger. Denne fleksibilitet giver mulighed for finjustering baseret på specifikke use cases og ydeevnekrav.

Endelig er dynger selvjusterende. Når elementer tilføjes eller fjernes, omarrangerer strukturen sig selv for at bevare dens egenskaber. Denne dynamiske balancering sikrer, at heapen til enhver tid forbliver optimeret til dens kerneoperationer.

Rådgivning: Disse egenskaber gjorde heap-datastruktur til en god egnethed til en effektiv sorteringsalgoritme - heap-sortering. For at lære mere om heap-sortering i Python, læs vores "Heap Sorter i Python" artiklen.

Efterhånden som vi dykker dybere ned i Pythons implementering og praktiske applikationer, vil det sande potentiale af dynger udfolde sig foran os.

Typer af dynger

Ikke alle dynger er skabt lige. Afhængigt af deres rækkefølge og strukturelle egenskaber kan dynger kategoriseres i forskellige typer, hver med sit eget sæt af applikationer og fordele. De to hovedkategorier er max bunke , min bunke.

Det mest karakteristiske træk ved en max bunke er, at værdien af ​​en given node er større end eller lig med værdierne for dens børn. Dette sikrer, at det største element i bunken altid ligger ved roden. En sådan struktur er især nyttig, når der er behov for hyppigt at få adgang til det maksimale element, som i visse prioritetskøimplementeringer.

Modstykket til den maksimale heap, en min bunke sikrer, at værdien af ​​en given node er mindre end eller lig med værdierne for dens børn. Dette placerer det mindste element i bunken ved roden. Mine heaps er uvurderlige i scenarier, hvor det mindste element er af største betydning, såsom i algoritmer, der beskæftiger sig med databehandling i realtid.

Ud over disse primære kategorier kan dynger også skelnes ud fra deres forgreningsfaktor:

Mens binære dynger er de mest almindelige, hvor hver forælder højst har to børn, kan begrebet dynger udvides til noder med mere end to børn. I en d-ær bunke, hver node har højst d børn. Denne variation kan optimeres til specifikke scenarier, såsom at reducere træets højde for at fremskynde visse operationer.

Binomial bunke er et sæt binomiale træer, der er defineret rekursivt. Binomiale heaps bruges i prioritetskøimplementeringer og tilbyder effektive fletteoperationer.

Opkaldt efter den berømte Fibonacci-sekvens, den Fibonacci bunke tilbyder bedre amortiserede driftstider for mange operationer sammenlignet med binære eller binomiale dynger. De er især nyttige i netværksoptimeringsalgoritmer.

Pythons Heap-implementering – Den heapq Moduler

Python tilbyder et indbygget modul til heap-operationer - den heapq modul. Dette modul giver en samling af heap-relaterede funktioner, der giver udviklere mulighed for at transformere lister til heaps og udføre forskellige heap-operationer uden behov for en tilpasset implementering. Lad os dykke ned i nuancerne i dette modul, og hvordan det giver dig dyngernes kraft.

heapq modul giver ikke en særskilt heap-datatype. I stedet tilbyder det funktioner, der fungerer på almindelige Python-lister, transformerer og behandler dem som binære dynger.

Denne tilgang er både hukommelseseffektiv og integreres problemfrit med Pythons eksisterende datastrukturer.

Det betyder det dynger er repræsenteret som lister in heapq. Skønheden ved denne repræsentation er dens enkelhed - det nul-baserede listeindekssystem fungerer som et implicit binært træ. For ethvert givet element ved position i, dens:

  • Venstre barn er i position 2*i + 1
  • Højre barn er i position 2*i + 2
  • Forældrenode er i position (i-1)//2

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

Denne implicitte struktur sikrer, at der ikke er behov for en separat nodebaseret binær trærepræsentation, hvilket gør operationer ligetil og hukommelsesbrug minimal.

Rumkompleksitet: Dynger er typisk implementeret som binære træer, men kræver ikke opbevaring af eksplicitte pointere til underordnede noder. Dette gør dem pladseffektive med en pladskompleksitet på O (n) til opbevaring af n elementer.

Det er vigtigt at bemærke, at heapq modul opretter min. dynger som standard. Det betyder, at det mindste element altid er ved roden (eller den første position på listen). Hvis du har brug for en maksimal bunke, skal du vende rækkefølgen ved at gange elementer med -1 eller brug en brugerdefineret sammenligningsfunktion.

Pythons heapq modul giver en række funktioner, der giver udviklere mulighed for at udføre forskellige heap-operationer på lister.

Bemærk: At bruge heapq modul i din applikation, skal du importere det ved hjælp af simple import heapq.

I de følgende sektioner vil vi dykke dybt ned i hver af disse grundlæggende operationer og udforske deres mekanik og anvendelsesmuligheder.

Hvordan man forvandler en liste til en bunke

heapify() funktion er udgangspunktet for mange heap-relaterede opgaver. Det tager en iterabel (typisk en liste) og omarrangerer dens elementer på plads for at tilfredsstille egenskaberne for en min heap:

Tjek vores praktiske, praktiske guide til at lære Git, med bedste praksis, brancheaccepterede standarder og inkluderet snydeark. Stop med at google Git-kommandoer og faktisk lærer det!

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

Dette vil udsende en omarrangeret liste, der repræsenterer en gyldig min. heap:

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

Tidskompleksitet: Konvertering af en uordnet liste til en bunke ved hjælp af heapify funktion er en O (n) operation. Dette kan virke kontraintuitivt, som man kunne forvente, at det var O (nlogn), men på grund af træstrukturens egenskaber kan det opnås i lineær tid.

Sådan tilføjer du et element til heapen

heappush() funktion giver dig mulighed for at indsætte et nyt element i heapen, mens du bibeholder heapens egenskaber:

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

Ved at køre koden får du en liste over elementer, der vedligeholder min heap-egenskaben:

[3, 5, 7]

Tidskompleksitet: Indsættelsesoperationen i en heap, som involverer at placere et nyt element i heapen og samtidig bibeholde heap-egenskaben, har en tidskompleksitet på O (logn). Det skyldes, at grundstoffet i værste fald skal rejse fra bladet til roden.

Sådan fjerner og returnerer det mindste element fra dyngen

heappop() funktion udtrækker og returnerer det mindste element fra heapen (roden i en min heap). Efter fjernelse sikrer det, at listen forbliver en gyldig bunke:

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

Bemærk: heappop() er uvurderlig i algoritmer, der kræver behandlingselementer i stigende rækkefølge, som Heap Sort-algoritmen, eller ved implementering af prioritetskøer, hvor opgaver udføres baseret på deres hastende karakter.

Dette vil udlæse det mindste element og den resterende liste:

1
[3, 7, 5, 9]

Her, 1 er det mindste element fra heap, og den resterende liste har bevaret heap-egenskaben, selv efter vi fjernede 1.

Tidskompleksitet: Fjernelse af rodelementet (som er det mindste i en min hob eller størst i en max heap) og omorganisering af heapen tager også O (logn) tid.

Sådan skubbes en ny genstand og pop den mindste genstand

heappushpop() funktion er en kombineret operation, der skubber et nyt emne ind på heapen og derefter popper og returnerer det mindste emne fra heapen:

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

Dette vil output 3, det mindste element, og print det nye ud heap liste, der nu omfatter 4 mens du bibeholder heap-egenskaben:

3
[4, 5, 7, 9]

Bemærk: Brug af heappushpop() funktion er mere effektiv end at udføre operationer med at skubbe et nyt element og poppe det mindste separat.

Sådan udskifter du den mindste genstand og skubber en ny genstand

heapreplace() funktionen åbner det mindste element og skubber et nyt element ind på dyngen, alt sammen i én effektiv operation:

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

Dette udskrives 1, det mindste element, og listen inkluderer nu 4 og vedligeholder heap-egenskaben:

1
[4, 5, 7, 9]

Bemærk: heapreplace() er en fordel i streaming-scenarier, hvor du vil erstatte det nuværende mindste element med en ny værdi, såsom i rullende vinduesoperationer eller databehandlingsopgaver i realtid.

At finde flere ekstremer i Python's Heap

nlargest(n, iterable[, key]) , nsmallest(n, iterable[, key]) funktioner er designet til at hente flere største eller mindste elementer fra en iterabel. De kan være mere effektive end at sortere hele den iterable, når du kun har brug for nogle få ekstreme værdier. Sig for eksempel, at du har følgende liste, og du vil finde tre mindste og tre største værdier på listen:

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

Her, nlargest() , nsmallest() funktioner kan være nyttige:

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

Dette vil give dig to lister – den ene indeholder de tre største værdier og den anden indeholder de tre mindste værdier fra data liste:

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

Sådan bygger du din brugerdefinerede bunke

Mens Python's heapq modul giver et robust sæt værktøjer til at arbejde med heaps, der er scenarier, hvor standard min heap-adfærd måske ikke er tilstrækkelig. Uanset om du ønsker at implementere en maks. heap eller har brug for en heap, der fungerer baseret på brugerdefinerede sammenligningsfunktioner, kan opbygning af en brugerdefineret heap være svaret. Lad os undersøge, hvordan man skræddersyer dynger til specifikke behov.

Implementering af en Max Heap vha heapq

Som standard heapq skaber min dynger. Men med et simpelt trick, kan du bruge det til at implementere en max heap. Ideen er at invertere rækkefølgen af ​​elementer ved at gange dem med -1 før du tilføjer dem til bunken:

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]

Med denne tilgang bliver det største tal (i form af absolut værdi) det mindste, hvilket tillader heapq funktioner til at opretholde en maksimal heap-struktur.

Dynger med brugerdefinerede sammenligningsfunktioner

Nogle gange har du måske brug for en bunke, der ikke kun sammenlignes baseret på den naturlige rækkefølge af elementer. For eksempel, hvis du arbejder med komplekse objekter eller har specifikke sorteringskriterier, bliver en tilpasset sammenligningsfunktion vigtig.

For at opnå dette kan du indpakke elementer i en hjælperklasse, der tilsidesætter sammenligningsoperatorerne:

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

Med denne opsætning kan du definere enhver brugerdefineret komparatorfunktion og bruge den med heapen.

Konklusion

Heaps tilbyder forudsigelig ydeevne til mange operationer, hvilket gør dem til et pålideligt valg til prioritetsbaserede opgaver. Det er dog vigtigt at overveje de specifikke krav og egenskaber ved den aktuelle applikation. I nogle tilfælde kan justering af heapens implementering eller endda vælge alternative datastrukturer give bedre ydelse i den virkelige verden.

Dynger, som vi har rejst igennem, er mere end blot endnu en datastruktur. De repræsenterer et sammenløb af effektivitet, struktur og tilpasningsevne. Fra deres grundlæggende egenskaber til deres implementering i Pythons heapq modul tilbyder heaps en robust løsning på et utal af beregningsmæssige udfordringer, især dem, der er centreret omkring prioritet.

Tidsstempel:

Mere fra Stablemisbrug