2.4 Prioritetskøer

Vi betragter nu yderligere samlingstype, prioritetskøen, som kan opfattes som en mere fleskibel struktur end stakken og køen. Elementerne i en prioritetskø skal være sammenlignelige, dvs. at det giver mening at tale om et “største” element i køen. Prioritetskøen understøtter to operationer: fjernelse af maksimum og indsættelse. Til sammenligning fjerner køen det først indsatte element, stakken fjerner det sidst indsatte element og prioritetskøen fjerner et største element, uanset hvornår det blev sat ind.

Programmeringsgrænseflade.

Vores konvention er at sammenligne nøgler udelukkende med metoden less(), ganske som vi gjorde for sortering. Hvis der altså er flere poster med samme maksimale nøgle, henviser maksimum til en vilkårlig blandt disse poster med størst nøgler. Programmeringsgrænsefladen afrundes med konstruktører og en metode til at afgøre, om køen er tom. For fleksibilitetens skyld implementerer vi datastrukturen for en generiske nøgletype Key som implementerer sammenlignergrænsefladen Comparable.

public class MaxPQ<Key extends Comparable<Key>>
MaxPQ() en tom prioritetskø
MaxPQ(int max) en tom prioritetskø med initialkapacitet max
MaxPQ(Key[] a) en prioritetskø af nøglerne i a[]
voidinsert(Key v)indsæt en nøgle
Keymax()returnér en største nøgle
KeydelMax()returnér og fjern en største nøgle
booleanisEmpty()er prioritetskøen tom?
intsize()antal elementer i prioritetskøen

Programmet TopM.java er en prioritetskøklient, som fra standardindput læser et heltal M og en følge transaktioner og skriver de M største transaktioner.

Grundlæggende implementationer.

De grundlæggende datastrukturer fra kapitel 2 giver os fire umiddelare udgangspunkter for implementationen af prioritetskøer.

operation argumentreturværdistørrelseindhold (ej sorteret) indhold (sorteret)
indsæt P1PP
indsæt Q2P QP Q
indsæt E3P Q EE P Q
fjern maksimum Q2P EE P
indsæt X3P E XE P X
indsæt A4P E X AA E P X
indsæt M5P E X A MA E M P X
fjern maksimum X4P E M AA E M P
indsæt P5P E M A PA E M P P
indsæt L6P E M A P LA E L M P P
indsæt E7P E M A P L EA E E L M P P
fjern maksimum P6E E M A P L A E E L M

De grundlæggende implementationer foroven har alle den egenskab, at værstefaldskøretiden for enten indsættelse eller fjernelse af maksimum er lineær. Vi skal nu betragte en implementation hvor begge operationer er garanteret hurtige.

Hobe.

En binærhob er en datastruktur, som understøtter priotetskøoperationerne på en effektiv måde. I en binærhob er elementerne gemt i en række, sådan at hver nøgle er større end eller lig med nøglerne på to andre, specifikke positioner, som vi skal bestemme nærmere forneden. Hver af disse nøgler er igen mindste lige så stor som to andre nøgler, og så videre. Denne ordning er nemt at visualisere, når vi betragter nøglerne i et binærtræ, hvor en nøgles to børn er de to nøgler, der skal være mindre.

Definition (hobinvariant). Et binærtræ af nøgler er hobordnet, hvis hver knudes nøgle er større end eller lig med dens børns nøgler.

Påstand. Roden i et hobordnet binærtræ indeholder en største nøgle.

Det giver mening at definere hobordning for vilkårlige, rodfæstede træer. Men vi vil først og fremmest betragte fuldstændige binærtræer.

Heap representations
Vi kan repræsentere fuldstændige binærtræer sekventielt i en række ved at anbringe knuderne lag for lag: Roden lægges på plads 1, dens børn på pladserne 2 og 3, deres børn på pladserne 4, 5, 6 og 7, og så videre.

Definition. En binærhob er en mængde af knuder med nøgler, der er anbragt i et fuldstændigt, hobordnet binærtræ, repræsenteret lag for lag i en række. (Rækkens første plads er uanvendt).

Heap representations

Ifølge vores konventioner ligger forælderen for en knude på plads \(k\) i en hob på plads \(k/2\). Knudens to børn ligger på pladserne \(2k\) og \(2k + 1\). Vi kan altså navigere op og ned ved enkle beregninger på rækkeindices: For at gå opad i træet fra a[k] sætter vi \(k\) til \(k/2\); for at gå nedad i træet sætter vi \(k\) til \(2k\) eller \(2k+1\).

Algoritmer på hobe.

Vi repræsenterer en hob af størrelse N som privat række pq[] af længde N+1 med pq[0] uanvendt og hobens elementer i pq[1] til pq[N]. Vi tilgår nøgler udelukkende ved brug af den private metode less() for at sammenligne og den private metode exch() for at bytte plads. Vores hoboperationer begynder alle med en enkel forandring af hoben, som muligvis forstyrrer hobordningen. Derefter genoprettes ordningen ved at navigere opad eller nedad i hoben og retablere hobinvarianten overalt. Vi kalder denne proces at genoprette hobordningen. Heap representations

Hobbaseret prioritetskø.

Synk- og svømmeoperationerne sink() og swim() udgør grundlaget for en effektiv implementation af grænsefladen for prioritetskøer. Dette er vist forneden og implementeret i MaxPQ.java og MinPQ.java for fjernelse af henholdsvis maksimum og minimum.

Heap operations


Påstand. I en prioritetskø med N elementer udfører hobalgoritmen højst 1 + lg N sammenligninger for indsættelse og højst 2 lg N sammenligninger for fjernelse af maksimum.

Praktiske overvejelser.

Vi afslutter vores studie af hobbaserede prioritetskøer med nogle praktiske overvejelser.

Hobsortering.

Enhver prioritetskør kan bruges som udgangspunkt for en sorteringsmetode. Vi indsætter alle nøgler i en prioritetskø, hvorefter vi udtager dem i sorteret orden ved at fjerne minimumsnølgen indtil køen er tom. Denne metode kaldes hobsortering, når man bruger en hob for prioritetskøen.

For sorteringsanvendelsen kan vi betragte svømning og synk direkte, i stedet for at gemme prioritetskøens hobrepræsentation. Hermed kan vi sortere en række uden ekstra pladsforbruge ved at lade hoben blive repræsenteret af den række, vi vil sortere. Hobsortering falder derved i to faser: hobkonstruktion, hvor vi omorganiserer den oprindelige række så den opfylder hobinvarianten, og nedsortering, hvor vi trækker elementerne ud af hoben i faldende ordning for at konstruere det sorterede resultat.

Trace of heapsort


Programmet Heap.java er en fuldstændig implementation af hobsortering. Forneden vises en kørslel med rækkeindholdet efter hvert synk.

Trace of heapsort


Påstand. Synkbaseret hobkonstruktion tager lineær tid.


Påstand. Hobsortering bruger højst 2N lg N sammenligninger og ombytninger for at sortere N elementer.

Synk til bunden, swøm op. Floyd observerede i 1964, at de fleste elementer, der sættes ind i hoben under nedsorteringen, synker hele vejen til bunden alligevel. Vi kan derfor spare sammenligninger ved at lade være med at undersøge, om elementet er synket på plads: vi lader det bytte plads med det største af børnene indtil det når bunden. Derefter lader vi det svømme opad i hoben til den korrekte plads. Denne idé halverer asymptotisk antallet af sammenligninger, men kræver til gengæld lidt ekstra bogholderi.