Föreläsning 13 Innehåll Prioritetsköer och heapar Prioritetsköer och heapar ADT prioritetskö Heapar Implementering av prioritetskö med heap Klassen PriorityQueue i java.util Programexempel LPT-algoritmen Sortering med hjälp av heap Datavetenskap (LTH) Föreläsning 13 VT 2019 1 / 48
ADT Prioritetskö I en prioritetskö ska finnas operationer för att sätta in element. ta reda på det högst prioriterade elementet (minsta). ta bort det högst prioriterade elementet (minsta). Elementen innehåller ett eller flera attribut som modellerar elementets prioritet. Jämförelser baseras på prioriteten. Dubbletter är tillåtna. Dvs. flera element kan ha samma prioritet. Datavetenskap (LTH) Föreläsning 13 VT 2019 2 / 48
Prioritetskö vs FIFO-kö Prioritetskö det viktigaste först Borttagning avser alltid mest prioriterade (minsta) elementet. FIFO-kö bevarar tidsordning First In First Out Borttagning avser alltid äldsta elementet. Datavetenskap (LTH) Föreläsning 13 VT 2019 3 / 48
Prioritetskö Exempel på användning Väntande patienter på en akutmottagning Patienterna beskrivs av en klass som innehåller ett heltalsattribut som anger prioritet. Lågt nummer = hög prioritet. Processer som köar för att få tillgång till CPU i dator. Olika algoritmer finns, t ex: högsta prioritet kortaste jobb kortast återstående tid (First-come-first-served använd FIFO-kö istället) Sortering heapsort Prioritetsköer används också som hjälpmedel i vissa algoritmer, t ex: grafalgoritmer (Dijkstras algoritm kortaste väg m.m.) ingår i en följande kurs Datavetenskap (LTH) Föreläsning 13 VT 2019 4 / 48
Diskutera Ge några förslag på hur man kan implementera en prioritetskö. Datavetenskap (LTH) Föreläsning 13 VT 2019 5 / 48
Prioritetskö - implementering Lista? Enkellänkad lista Sorterad peek och poll blir O(1) offer blir O(n) rätt plats för elementet måste letas upp Osorterad peek och poll blir O(n) minsta element måste letas upp offer blir O(1) elementet kan sättas in först Nackdel: Vissa operationer blir långsamma. Datavetenskap (LTH) Föreläsning 13 VT 2019 6 / 48
Prioritetskö - implementering forts. Balanserat binärt sökträd: Fungerar bara om prioriteterna är unika. Minsta elementet finns längst ner till vänster. Alla operationerna blir O(log n). Nackdel: Klarar inte dubbletter. Heap Vanligtvis använder man en heap för att implementera prioritetskön. Datavetenskap (LTH) Föreläsning 13 VT 2019 7 / 48
Heap Definition Heap En heap är ett komplett binärt träd där varje nod innehåller ett element som är barnens element. Trädet har alltså formen För varje delträd gäller att roten innehåller det minsta elementet. Datavetenskap (LTH) Föreläsning 13 VT 2019 8 / 48
Heap Exempel och motexempel 2 2 2 3 6 3 6 5 6 5 7 5 7 3 7 Heap Ingen heap. Fel form, men korrekt ordning Ingen heap. Rätt form men ej korrekt ordning Datavetenskap (LTH) Föreläsning 13 VT 2019 9 / 48
Heap Representation En heap kan med fördel lagras i en vektor. Roten finns på plats 0. Barnen till noden på plats i finns på platserna 2i + 1 och 2i + 2 i vektorn. Noden på plats i har alltså sin förälder på plats (i 1)/2. 2 3 6 2 3 6 5 7 0 1 2 3 4 5 7 Datavetenskap (LTH) Föreläsning 13 VT 2019 10 / 48
Implementering av PriorityQueue public class PriorityQueue<E> implements Queue<E> { private E[] queue; private int size;... konstruktorer... } boolean offer(e x) {...} E peek() {...} E poll() {...}... Datavetenskap (LTH) Föreläsning 13 VT 2019 11 / 48
offer Implementering Nya elementet placeras på första lediga plats i vektorn. Detta ger rätt form på trädet. Sedan byten uppåt tills rätt ordning. Kallas percolate up eller addleaf. Ex: Sätt in element med nyckel 1 i heapen: 2 3 6 2 3 6 5 7 5 7 Datavetenskap (LTH) Föreläsning 13 VT 2019 12 / 48
offer Implementering, forts 2 2 Byt! 1 Klart! 3 6 3 1 3 2 5 7 1 5 7 6 5 7 6 Byt! 2 3 6 5 7 1 2 3 1 5 7 6 1 3 2 5 7 6 Datavetenskap (LTH) Föreläsning 13 VT 2019 13 / 48
offer Tidskomplexitet Eftersom en heap är ett komplett binärt träd så gäller h log n där n är antal noder och h är höjden. Den nya noden kan i värsta fall behöva jämföras (och bytas) med alla element på vägen upp till roten. De är h st. Således är antalet jämförelser i värsta fall log n. I medeltal blir det inte så många byten. Man har visat att det blir O(1) byten i medelfall. Datavetenskap (LTH) Föreläsning 13 VT 2019 14 / 48
peek Implementering peek: Minsta element finns på plats 0 i vektorn! Blir O(1)-operation. Datavetenskap (LTH) Föreläsning 13 VT 2019 15 / 48
poll Implementering Tag bort noden på plats 0 i vektorn. Ersätt med den som finns på sista plats. Ger rätt form, men roten har nu troligtvis fel storleksförhållande till sina barn. Byt med minsta av barnen tills ordningen ok. Kallas percolate down eller addroot. Ex: Utför poll() på: 1 3 4 1 3 4 5 7 6 5 7 6 Datavetenskap (LTH) Föreläsning 13 VT 2019 16 / 48
poll Implementering, forts Byt med minsta av barnen! Byt med minsta av barnen! Klart! 6 3 3 3 4 6 4 5 4 5 7 5 7 6 7 6 3 4 5 7 3 6 4 5 7 3 5 4 6 7 Datavetenskap (LTH) Föreläsning 13 VT 2019 17 / 48
poll Tidskomplexitet I värsta fall får jämförelse med barnen och byten upprepas ända ner till ett löv. Värstafallskostnad för poll blir således O(log n). Eftersom det är en nod långt nedifrån (och alltså sannolikt en nod med ett stort element) i trädet som sätts in i roten och byts nedåt så kommer bytena ofta att behöva fortsätta ända ned till ett löv. Medelfallet kan också visas vara O(log n). Datavetenskap (LTH) Föreläsning 13 VT 2019 18 / 48
Diskutera Är en heap stabil? En prioritetskö är stabil om element med lika prioritet plockas ut i samma ordning som de sattes in. Är en heap stabil? Prova att sätta in tre element med samma prioritet (t.ex. 1) i en heap. I vilken ordning plockas de ut? Datavetenskap (LTH) Föreläsning 13 VT 2019 19 / 48
Klassen PriorityQueue i java.util Interfacet Queue<E> används både för prioritetsköer och vanliga FIFO-köer. En konkret klass PriorityQueue<E> implementerar Queue<E>: public class PriorityQueue<E> implements Queue<E> { boolean offer(e x) {...} E peek() {...} E poll() {...}... } Konvention: lågt värde på prioritetsattribut anger hög prioritet. peek() returnerar minsta elementet i kön. poll() tar bort och returnerar minsta elementet i kön. Datavetenskap (LTH) Föreläsning 13 VT 2019 20 / 48
Klassen PriorityQueue i java.util forts. Det finns flera konstruktorer, bl. a: 1 PriorityQueue() Förutsätter att elementen implementerar Comparable, annars genereras ClassCastException. Inuti PriorityQueue jämförs elementen med compareto. 2 PriorityQueue(Comparator<? super E> c) Elementen jämförs med hjälp av komparatorn c. Observera att en iterering genom prioritetskön inte ger elementen i prioritetsordning. Upprepade borttagningar (poll) ger elementen i prioritetsordning. Datavetenskap (LTH) Föreläsning 13 VT 2019 21 / 48
Problemexempel: schemaläggning av maskiner Vi har m identiska maskiner och n jobb att utföra. Tiden för att utföra jobb i är t i. En maskin kan bara göra ett jobb i taget. Ett enskilt jobb kan inte delas mellan olika maskiner. Ett jobb som en maskin börjat utföra görs färdigt utan avbrott. Hur ska jobben allokeras till maskinerna för att tiden det tar att slutföra alla jobb ska bli så kort som möjligt? Datavetenskap (LTH) Föreläsning 13 VT 2019 22 / 48
Schemaläggning av maskiner Man känner inte till någon effektiv algoritm som löser problemet: Att prova alla möjligheter går men kostar exponentiell exekveringstid. Approximativa algoritmer måste användas i praktiken för stora probleminstanser. En sådan är LPT-algoritmen Longest Processing Time first. Datavetenskap (LTH) Föreläsning 13 VT 2019 23 / 48
LPT-algoritmen Longest Processing Time Jobben allokeras till maskiner efter avtagande tidsåtgång. Jobbet allokeras till den maskin som har minst att göra (blir ledig tidigast). Ex: 3 maskiner M 1..M 3 och 7 jobb j 1..j 7 med tidsåtgång 2, 14, 4, 16, 6, 5, 3. 16 ( j ) M 1 4 M 2 14 ( j ) 2 3 ( j ) 7 M 3 6 ( j ) 5 ( j ) 4 ( j ) 2 ( j ) 5 6 3 1 Total tidsåtgång för att avsluta alla jobb: 17 Datavetenskap (LTH) Föreläsning 13 VT 2019 24 / 48
LPT-algoritmen Ger inte alltid den optimala lösningen Ex: 2 maskiner och 5 jobb med tidskrav 6, 6, 5, 5, 5. Total tidsåtgång för att avsluta alla jobb blir 16: M 1 6 5 5 M 2 6 5 Men bättre lösning finns tidsåtgång 15: M 1 5 5 5 M 2 6 6 Datavetenskap (LTH) Föreläsning 13 VT 2019 25 / 48
LPT-algoritmen Implementering Maskinerna sätts in i en prioritetskö. Som prioritet fungerar sammanlagda tiden för maskinens jobb = tidpunkt då maskinen blir ledig. Alla maskiner är lediga från början. De har då tid 0. sortera jobben med efter avtagande tid skapa en prioritetskö q låt alla maskiner vara lediga och sätt in dem i q för varje jobb j m = q.poll(); lägg till jobbet j till maskinen m q.offer(m); Datavetenskap (LTH) Föreläsning 13 VT 2019 26 / 48
LPT-algoritmen Fallet antal jobb antal maskiner I specialfallet antal jobb antal maskiner (n m) kan algoritmen förenklas: Alla jobb kan då sättas igång samtidigt på var sin maskin. Det blir det längsta jobbet som bestämmer tidsåtgången. Vi behöver alltså bara söka maximum i listan av jobb. Datavetenskap (LTH) Föreläsning 13 VT 2019 27 / 48
LPT-algoritmen tidskomplexitet Allmänna fallet Vi antar att antal jobb > antal maskiner (n > m). Sortera jobben efter avtagande tidsåtgång kostar O(n log n) med en effektiv metod Bygga en heap och sätta in de m maskinerna kostar O(m) eftersom alla maskinernas prioritet är lika initialt poll n gånger och offer n gånger i heapen av storlek m kostnad O(n log m) Totalt: O(n log n) Datavetenskap (LTH) Föreläsning 13 VT 2019 28 / 48
Diskutera Man kan använda en prioritetskö för att sortera. Hur? Fördelar/nackdelar? Datavetenskap (LTH) Föreläsning 13 VT 2019 29 / 48
Sortering med hjälp av prioritetskö Sorteringsidé, för att sortera en vektor a: PriorityQueue<E> myq = new PriorityQueue<E>(); for (int i = 0; i < a.length; i++) { myq.offer(a[i]); } for (int i = 0; i < a.length; i++) { a[i] = myq.poll(); } Tidskomplexitet (n = a.length): n gånger offer och n gånger poll ger O(n log n) Datavetenskap (LTH) Föreläsning 13 VT 2019 30 / 48
Sortering med hjälp av prioritetskö Kommentarer Elementen flyttas från vektorn till en annan intern vektor som representerar kön. Det behövs alltså extra minnesutrymme proportionellt mot storleken på den mängd som ska sorteras. Prioritetskön byggs med successiva offer. Det går att bygga om en osorterad vektor till en heap på effektivare sätt. Båda punkterna kan förbättras om vi har tillgång till den vektor som representerar kön. Datavetenskap (LTH) Föreläsning 13 VT 2019 31 / 48
Bygga heap från osorterad vektor Heapify (eller buildheap) Idén är att bygga om det träd vektorn representerar till en heap nedifrån och upp. Ex: Utgå från följande vektor: 90 150 130 10 80 20 Bygg en heap på plats i vektorn genom att utföra percolate down med början på den nod som finns på plats n/2 1 sedan på plats n/2 2,..., 0. 90 150 130 10 80 20 Börja alltså med percolate down på detta delträd! Datavetenskap (LTH) Föreläsning 13 VT 2019 32 / 48
Bygga heap från osorterad vektor Heapify (eller buildheap), forts Fortsätt med percolate down här: 90 150 20 10 80 130 Sedan percolate-down på roten, som efter byte (i detta fall med vänster barn) innebär att vi eventuellt måste fortsätta med underträd precis som i poll. 90 10 10 10 20 90 20 80 20 150 80 130 150 80 130 150 90 130 Datavetenskap (LTH) Föreläsning 13 VT 2019 33 / 48
Heapify - tidskomplexitet I heapify börjar vi på den näst nedersta nivån i trädet och ser till att dessa underträd blir heapordnade. Dessa träd har maximalt höjden 1 och det blir maximalt ett byte i vardera underträd. Sedan fortsätter vi på nivån över. Här finns färre noder, men underträden har maximalt höjden 2. De utför vardera därför maximalt 2 byten etc. Ju högre upp i trädet desto färre noder men desto fler byten nedåt i värsta fall. Man kan visa att heapify kostar O(n) där n är antalet element i vektorn. Datavetenskap (LTH) Föreläsning 13 VT 2019 34 / 48
Heapify - tidskomplexitet Forts Jämför med att bygga heapen med successiva offer: offer sätter in elementet sist i vektorn (längst ned i trädet) och därefter sker byten uppåt tills trädet är heapordnat. Tidiga insättningar på låga nivåer i trädet kan maximalt behöva flyttas färre steg uppåt än senare insättningar längre ned i trädet. Det är också färre noder på nivåer nära roten än på nivåer längre från roten, d.v.s. här riskerar många noder långa bytesskedjor. Datavetenskap (LTH) Föreläsning 13 VT 2019 35 / 48
Heapify - implementering Koden för heapify blir enkel: private void heapify() { for (int i = (size - 2) / 2; i >= 0; i--) { percolatedown(i); } } Sista elementet finns på plats size 1 i vektorn dess förälder finns på plats (size 2)/2. percolatedown(k) en metod som med start på noden på plats k i vektorn utför byten nedåt i heapen så länge ordningen är felaktig. (Denna metod används även av operationen poll.) Datavetenskap (LTH) Föreläsning 13 VT 2019 36 / 48
Sortering på plats i en vektor som representerar en minheap Gör successiva poll på minheapen. 10 20 80 20 80 130 150 90 130 150 90 10 80 20 150 90 130 20 80 130 150 90 20 80 130 150 90 10 Efter ett poll Den lediga platsen utnyttjas för att lagra 10. Datavetenskap (LTH) Föreläsning 13 VT 2019 37 / 48
Sortering på plats i en vektor som representerar en minheap Forts 80 90 130 150 80 90 130 150 10 80 90 130 150 20 10 150 130 90 80 20 10 Nästa poll tar bort 20. Det borttagna elementet 20 placeras på den lediga platsen. Etc... Slutligen har vi en vektor sorterad i omvänd ordning. Datavetenskap (LTH) Föreläsning 13 VT 2019 38 / 48
Heapsort Man kan få vektorn sorterad i växande ordning om man i stället bygger en max-heap, dvs en heap där förälderns nyckel barnens nycklar. Man tar sedan successivt ut största elementet. Ex: sortera en vektorn {10 80 20 130 150 90} Se följande bilder. Datavetenskap (LTH) Föreläsning 13 VT 2019 39 / 48
Heapsort Vi börjar med att utföra heapify på vektorn men nu så att föräldrar blir större än barn. 10 10 80 20 80 90 130 150 90 130 150 20 10 150 150 150 90 10 90 130 90 130 80 20 130 80 20 10 80 20 Datavetenskap (LTH) Föreläsning 13 VT 2019 40 / 48
Heapsort Den resulterande heapen är alltså: 130 150 90 Tag ut största talet (första platsen). Flytta sista noden (20) till luckan som då uppstår på plats 0. Flytta samtidigt den borttagna noden (150) till vektorns sista plats. Blir alltså byte mellan första och sista: 10 80 20 150 130 90 10 80 20 20 130 90 10 80 150 Datavetenskap (LTH) Föreläsning 13 VT 2019 41 / 48
Heapsort De n-1 första elementen i vektorn representerar trädet: 20 Återställ heapordningen i trädet genom percolate-down (blir två byten): 130 130 90 20 90 10 80 10 80 20 130 90 10 80 150 130 80 90 10 20 130 80 90 10 20 150 Datavetenskap (LTH) Föreläsning 13 VT 2019 42 / 48
Heapsort Tag bort det största elementet och byt med elementet på plats n-2: Återställ heapordningen i trädet genom percolate-down (blir ett byte): 20 80 90 10 130 150 90 80 20 Nu är två element på rätt plats. De n-2 första representerar trädet: 10 20 90 80 20 10 130 150 80 90 10 Etc... Datavetenskap (LTH) Föreläsning 13 VT 2019 43 / 48
Heapsort - sammanfattning 1 Bygg om vektorn till en maxheap (heapify). 2 Gör upprepade poll (lagra det borttagna elementet på den lediga platsen som uppstår i vektorn). Sorterar n element i en vektor på plats. Effektiv O(n log n) i värsta fall. Efter k steg i algoritmen är de k största elementen sorterade. Metoden kan alltså avbrytas om vi endast vill ta reda på de k största elementen. Datavetenskap (LTH) Föreläsning 13 VT 2019 44 / 48
Diskutera Antag att prioriteterna är heltal i ett känt begränsat intervall 1..k, t ex 1..10. Finns det något bättre (snabbare, enklare) sätt än heap att implementera en prioritetskö i just detta specialfall? Datavetenskap (LTH) Föreläsning 13 VT 2019 45 / 48
Specialfall - enklare implementering Specialfall Om prioriteterna är heltal i ett känt begränsat intervall, t.ex. 1..k: Använd en vektor med listor. Vektorstorlek k + 1. Lägg element med prioriteten j i den lista som finns på plats j i vektorn. Operationerna får konstant tidskomplexitet (k är en konstant). Men ingen generell lösning. Datavetenskap (LTH) Föreläsning 13 VT 2019 46 / 48
Alternativ representation av prioritetsköer Många algoritmer som använder prioritetsköer som hjälpmedel behöver fler operationer: decreasekey(...), increasekey(...) höj/sänk prioriteten på ett element som befinner sig i kön. Det går att använda en heap, men elementen som sätts in måste innehålla information om på vilken plats de befinner sig i vektorn så att de kan flyttas från denna till rätt plats med hänsyn till ordningen. (Utan information om elementets plats skulle man behöva söka igenom vektorn efter elementet). Datavetenskap (LTH) Föreläsning 13 VT 2019 47 / 48
Prioritetsköer och heapar Exempel på vad du ska kunna Redogöra för vilka operationer som skall finnas i den abstrakta datatypen prioritetskö. Redogöra för olika sätt att implementera en prioritetskö och kunna jämföra dem med avseende på tidskomplexitet. Använda klassen java.util.priorityqueue Förklara begreppet heap Förklara hur en heap kan implementeras med hjälp av en vektor Förklara hur insättning, borttagning och sökning efter högst prioriterat element går till i en heap och ange dessa metoders tidskomplexitet. Förklara hur man bygger en heap på linjär tid från en osorterad samling Redogöra för algoritmen Heapsort och dess värstafallstid. Datavetenskap (LTH) Föreläsning 13 VT 2019 48 / 48