Prioritetsköer, heapar 31 13 Prioritetsköer, heapar U 101. En prioritetskö är en samling element där varje element har en prioritet (som används för att jämföra elementen med). Elementen plockas ut i prioritetsordning till skillnad mot en vanlig kö där elementen plockas ut i den ordning de satts in i kön. De operationer man ska kunna göra på en prioritetskö är sätta in element ta reda på det högst prioriterade elementet (minsta elementet) ta bort det högst prioriterade elementet (minsta elementet) U 102. Heap efter insättning av element med nycklarna: 2, 5, 1, 7, 9, 6, 3, 0, 8, 4. 0 1 2 5 4 6 3 7 8 9 U 103. En heap kan lagras i en vektor. Roten lagras på plats 0. Barnen till noden på plats i finns på platserna 2i + 1 och 2i + 2 i vektorn. Nod på plats i har alltså sin förälder på plats (i-1)/2). Heapen från uppgift U 102: 0 1 2 5 4 6 3 7 8 9 0 1 2 3 4 5 6 7 8 9 U 104. Tag bort noden på plats 0 i vektorn. Ersätt med den som finns på sista plats. Detta ger rätt form, men roten har nu troligtvis fel storleksförhållande till sina barn. Byt med minsta av barnen tills ordningen ok ( percolate down ). U 105. a) Antag t ex att vi sätter in tre lika element e 1, e 2 och e 3 (i den ordningen) i en heap. Heapens utseende efter dessa insättningar visas till vänster i figuren nedan. Antag nu att vi gör en borttagning. Det blir då e 1 som tas ut. Efter borttagningen har heapen det utseende som visas till höger i figuren nedan. Nästa borttagning kommer därför att ta ut e 3. Detta element är yngre än e 2. Alltså är heapen inte stabil. e 1 e 3 e 2 e 3 e 2 b) Man kan sätta in element som består av prioritet plus ett nummer. Man numrerar sina element 1,2,... efterhand som man sätter in dem. Vidare definierar man comparetometoden så att den jämför på prioritet i första hand och nummer i andra hand. Av två element med lika prioritet kommer ett som är senare insatt då att anses vara större än det tidigare insatta. Då kommer element med lika prioritet att komma ut ur heapen i den ordning de sattes in.
32 Prioritetsköer, heapar U 106. I en heap kan man snabbt hitta minsta elementet. Sökning av ett godtyckligt element blir däremot dyrare. Vi kan inte söka oss ner på en gren som i ett sökträd, en heap är ju inte ordnad på samma sätt. Vi måste söka i både vänster och höger underträd tills vi eventuellt hittar elementet. I värsta fall behöver vi söka igenom hela heapen, vilket kostar O(n). U 107. a) Om trädet är skevt t.ex. om alla noder bara har höger barn så kommer noderna att hamna på platserna 0, 2, 6,..., 2 i 2,..., 2 n 2 i vektorn. I det andra fallet inträffar värsta fallet när noden på nivå k + 1 är höger barn till noden längst till höger på nivå k. Noden på nivå 1 finns på plats 0 i vektorn. Noderna på nivå 2 finns på platserna 1 och 2, noderna på nivå 3 på platserna 3, 4, 5 och 6 etc. Noderna på nivå i finns alltså på platserna 2 i 1 1... 2 i 2. Alla noder på nivåerna 1..k kommer därför att fylla platserna 1..2 k 2. Lägger vi till ett höger barn till den sista noden hamnar det på plats 2 k+1 2 d.v.s. vi behöver ungefär dubbelt så stor vektor trots att vi bara lägger till en enda nod på sista nivån. b) Om trädet är tomt ska det nya elementet placeras i roten. Om trädet har en nod ska det nya elementet placeras som vänster barn till roten, och om trädet har två noder ska det nya elementet placeras som höger barn till roten. Låt en båge från en nod till dess vänstra barn representera en nolla och en båge från en nod till dess högra barn representera en etta. Vägen för den andra noden beskrivs då av en nolla och vägen för den tredje noden av en etta. Om vi går vidare och utgår från ett träd med tre noder så ska den fjärde noden placeras som vänster barnbarn till roten. Denna väg motsvarar sekvensen 00. Nästa nods väg beskrivs av 01 och nästa av 10 och den sjunde nodens väg är 11. Om vi låter vägen till roten beskrivas av en etta får vi i stället sekvensenra 100, 101, 110 och 111 d.v.s. den binära representationen av 4, 5, 6 och 7. Varje nods plats beskrivs alltså på detta sätt av den binära representationen av antalet noder efter insättningen. U 108. I stället för en heap kan man använda t.ex. ett balanserat binärt sökträd för att representera en prioritetskö. Trädet sorteras då efter prioriteter. Man måste modifiera trädimplementeringen så att dubbletter kan sättas in. Detta kan man göra genom att man vid likhet mellan element alltid väljer att göra insättning i t.ex. höger underträd. Vi vet sedan tidigare att insättning kostar O( 2 logn) i ett balanserat binärt sökträd. Om vi ska använda trädet som en prioritetskö behöver vi också en metod för att ta bort minsta elementet ur trädet. Detta element finns längst ner till vänster i trädet, d.v.s. man hittar det genom att utgående från roten flytta sig nedåt med hjälp av referenserna till vänster barn. Borttagning kan inte kosta mer än O( 2 logn) i ett balanserat träd. Man kan också använda listor, sorterade eller osorterade. Har man en osorterad lista blir operationerna för att söka minsta och ta bort minsta långsamma (O(n)) men insättning blir O(1). För en sorterad lista är det tvärtom. U 109. Man kan införa en vektor av listor. Vektorns storlek = antalet olika prioriteter. Ett elements prioritet avgör i vilken lista det placeras. Insättning blir O(1). Tag bort minsta och sök minsta blir också O(1). Man måste visserligen söka upp första icke-tomma listan i vektorn, men vektorns storlek är en konstant. Man åstadkommer stabilitet genom att sätta in ett nytt element sist i den lista där det hör hemma. Det kräver då att man har en listimplementation där insättning sist kostar O(1).
Prioritetsköer, heapar 33 U 110. a) public class Patient implements Comparable<Patient> { private static int total = 0; private String firstname; private String lastname; private String personnbr; private int prio; private int number; public Patient(String firstname, String lastname, String personnbr, int prio) { this.firstname = firstname; this.lastname = lastname; this.personnbr = personnbr; this.prio = prio; total++; number = total; public int compareto(patient rhs) { if (prio == rhs.prio) { return number - rhs.number; else { return prio - rhs.prio; public boolean equals(object rhs) { if (rhs instanceof Patient) { return compareto((patient) rhs) == 0; else { b) PriorityQueue<Patient> pq = new PriorityQueue<Patient>(); pq.offer(new Patient("Kalle", "Karlsson", "8503622-1213", 3)); pq.offer(new Patient("Lisa", "Svensson", "840312-1224", 2)); pq.offer(new Patient("Lena", "Nilsson", "820323-1224", 3)); U 111. a) public class PrioComparator implements Comparator<Patient> { public int compare(patient p1, Patient p2) { if (p1.getprio() == p2.getprio()) { return p1.getnumber() - p2.getnumber(); else { return p1.getprio() - p2.getprio(); Om vi förutsätter att metodern getnumber och getprio redan finns i klassen Patient behövs inga ytterligare förändringar. Klassen Patient ser ut så här:
34 Prioritetsköer, heapar public class Patient implements Comparable<Patient> {... // attribut och konstruktor enligt tidigare public int compareto(patient rhs) { return personnbr.compareto(rhs.personnbr); public boolean equals(object rhs) { if (rhs instanceof Patient) { return compareto((patient) rhs) == 0; else { public int getprio() { return prio; public int getnumber() { return number; b) PriorityQueue<Patient> pq = new PriorityQueue<Patient>(10, new PrioComparator()); U 112. a) /** * Skapar ett objekt som hanterar en kö för köpordrar och en kö för säljordrar * för aktien med id shareid. * @param shareid aktieslag public OrderQueues(String shareid) { this.shareid = shareid; buyorders = new PriorityQueue<Order>(10, new ReversePriceComparator()); sellorders = new PriorityQueue<Order>(10, new PriceComparator()); /** * Lägger till en köporder ifall matchande säljorder inte finns. * Om matchande säljorder finns tas säljordern bort och returneras. * @param buyorder köporder * @return matchande säljorder om sådan finns, i annat fall null public Order addbuyorder(order buyorder) { if (! sellorders.isempty() && buyorder.getprice() >= sellorders.peek().getprice()) { return sellorders.poll(); buyorders.offer(buyorder); return null; Det behövs också två klasser som implementerar Comparator:
Prioritetsköer, heapar 35 public class PriceComparator implements Comparator<Order> { public int compare(order order1, Order order2) { return Double.compare(order1.getPrice(), order2.getprice()); public class ReversePriceComparator implements Comparator<Order> { public int compare(order order1, Order order2) { return Double.compare(order2.getPrice(), order1.getprice()); Alternativ lösning: Man kan stryka den ena Comparator-klassen om man låter klassen Order implementera Comparable: public class Order implements Comparable<Order> {... public int compareto(order other) { return Double.compare(price, other.price); public boolean equals(object other) { if (!(other instanceof Order)) { return (compareto((order) other) == 0); I så fall skapas kön med säljordrar så här: sellorders = new PriorityQueue<Order>(); b) /** * Låter kunden customer lägga en köporder av aktieslaget shareid till * budpriset price. Genomför köpet om matchande säljorder finns, i annat * fall lagras köpordern i motsvarande orderkö. * @param customer kunden * @param shareid aktieslag * @param price budpris * @throws NoSuchElementException om det inte finns någon orderkö för * aktieslaget shareid. public void buy(customer customer, String shareid, double price) { Order buyorder = new Order(price, customer); OrderQueues share = q.get(shareid); if (share == null) { throw new NoSuchElementException(); Order matchingsellorder = share.addbuyorder(buyorder); if (matchingsellorder!= null) { execute(buyorder, matchingsellorder); c) I metoden addbuyorder utförs peek (som kostar O(1)) och sedan poll eller offer (som kostar O(logn)). Den totala tidskomplexiteten blir alltså O(logn).