5. Prioriteettijonot. 5. luku 236

Samankaltaiset tiedostot
5. Prioriteettijonot Prioriteettijonon abstrakti tietotyyppi

useampi ns. avain (tai vertailuavain) esim. opiskelijaa kuvaavassa alkiossa vaikkapa opintopistemäärä tai opiskelijanumero

lähtokohta: kahden O(h) korkuisen keon yhdistäminen uudella juurella vie O(h) operaatiota vrt. RemoveMinElem() keossa

Algoritmit 2. Luento 2 Ke Timo Männikkö

Algoritmit 2. Luento 2 To Timo Männikkö

Pinot, jonot, yleisemmin sekvenssit: kokoelma peräkkäisiä alkioita (lineaarinen järjestys) Yleisempi tilanne: alkioiden hierarkia

Algoritmit 2. Luento 3 Ti Timo Männikkö

Algoritmit 2. Luento 3 Ti Timo Männikkö

Algoritmit 1. Luento 7 Ti Timo Männikkö

Hakupuut. tässä luvussa tarkastelemme puita tiedon tallennusrakenteina

Algoritmit 1. Luento 10 Ke Timo Männikkö

AVL-puut. eräs tapa tasapainottaa binäärihakupuu siten, että korkeus on O(log n) kun puussa on n avainta

A TIETORAKENTEET JA ALGORITMIT

Algoritmit 1. Luento 3 Ti Timo Männikkö

Algoritmit 1. Luento 12 Ke Timo Männikkö

811312A Tietorakenteet ja algoritmit III Lajittelualgoritmeista

8. Lajittelu, joukot ja valinta

Tehtävän V.1 ratkaisuehdotus Tietorakenteet, syksy 2003

58131 Tietorakenteet ja algoritmit (kevät 2016) Ensimmäinen välikoe, malliratkaisut

Algoritmit 2. Luento 7 Ti Timo Männikkö

Algoritmit 1. Luento 12 Ti Timo Männikkö

TKT20001 Tietorakenteet ja algoritmit Erilliskoe , malliratkaisut (Jyrki Kivinen)

4. Sekvenssit Astetta soveltavat sekvenssit

Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

v 1 v 2 v 3 v 4 d lapsisolmua d 1 avainta lapsen v i alipuun avaimet k i 1 ja k i k 0 =, k d = Sisäsolmuissa vähint. yksi avain vähint.

58131 Tietorakenteet ja algoritmit (syksy 2015) Toinen välikoe, malliratkaisut

Algoritmit 2. Luento 5 Ti Timo Männikkö

Kaksiloppuinen jono D on abstrakti tietotyyppi, jolla on ainakin seuraavat 4 perusmetodia... PushFront(x): lisää tietoalkion x jonon eteen

Binäärihaun vertailujärjestys

7. Tasapainoitetut hakupuut

1 Puu, Keko ja Prioriteettijono

Algoritmit 2. Luento 4 To Timo Männikkö

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

private TreeMap<String, Opiskelija> nimella; private TreeMap<String, Opiskelija> numerolla;

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Algoritmit 2. Demot Timo Männikkö

18. Abstraktit tietotyypit 18.1

Tietorakenteet ja algoritmit - syksy

Tietorakenteet, laskuharjoitus 7, ratkaisuja

Sisällys. 18. Abstraktit tietotyypit. Johdanto. Johdanto

10. Painotetut graafit

Algoritmit 2. Luento 14 Ke Timo Männikkö

2. Seuraavassa kuvassa on verkon solmujen topologinen järjestys: x t v q z u s y w r. Kuva 1: Tehtävän 2 solmut järjestettynä topologisesti.

Tietorakenteet, laskuharjoitus 3, ratkaisuja

Algoritmit 1. Luento 5 Ti Timo Männikkö

A TIETORAKENTEET JA ALGORITMIT

Luku 8. Aluekyselyt. 8.1 Summataulukko

Algoritmit 1. Luento 6 Ke Timo Männikkö

4. Joukkojen käsittely

Tietorakenteet, laskuharjoitus 6,

811312A Tietorakenteet ja algoritmit II Perustietorakenteet

Algoritmi on periaatteellisella tasolla seuraava:

Algoritmit 2. Luento 5 Ti Timo Männikkö

6. Sanakirjat. 6. luku 298

Algoritmit 1. Luento 4 Ke Timo Männikkö

(p j b (i, j) + p i b (j, i)) (p j b (i, j) + p i (1 b (i, j)) p i. tähän. Palaamme sanakirjaongelmaan vielä tasoitetun analyysin yhteydessä.

Algoritmit 2. Luento 4 Ke Timo Männikkö

Taulukot. Taulukon määrittely ja käyttö. Taulukko metodin parametrina. Taulukon sisällön kopiointi toiseen taulukkoon. Taulukon lajittelu

Harjoitustyön testaus. Juha Taina

3. Hakupuut. B-puu on hakupuun laji, joka sopii mm. tietokantasovelluksiin, joissa rakenne on talletettu kiintolevylle eikä keskusmuistiin.

Algoritmit 2. Luento 6 Ke Timo Männikkö

Algoritmit 2. Luento 13 Ti Timo Männikkö

1. (a) Seuraava algoritmi tutkii, onko jokin luku taulukossa monta kertaa:

58131 Tietorakenteet (kevät 2009) Harjoitus 6, ratkaisuja (Antti Laaksonen)

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Miten käydä läpi puun alkiot (traversal)?

58131 Tietorakenteet (kevät 2009) Harjoitus 9, ratkaisuja (Antti Laaksonen)

Ohjelmoinnin perusteet Y Python

3. Binääripuu, Java-toteutus

Tarkennamme geneeristä painamiskorotusalgoritmia

(a) L on listan tunnussolmu, joten se ei voi olla null. Algoritmi lisäämiselle loppuun:

5. Keko. Tietorakenne keko eli kasa (heap) on tehokas toteutus abstraktille tietotyypille prioriteettijono, jonka operaatiot ovat seuraavat:

A ja B pelaavat sarjan pelejä. Sarjan voittaja on se, joka ensin voittaa n peliä.

Relaation ominaisuuksia. Ominaisuuksia koskevia lauseita Sulkeumat. Joukossa X määritelty relaatio R on. (ir) irrefleksiivinen, jos x Rx kaikilla x X,

Joukossa X määritelty relaatio R on. (ir) irrefleksiivinen, jos x Rx kaikilla x X,

Algoritmit 1. Luento 11 Ti Timo Männikkö

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

4 Tehokkuus ja algoritmien suunnittelu

811312A Tietorakenteet ja algoritmit, , Harjoitus 3, Ratkaisu

Kääreluokat (oppikirjan luku 9.4) (Wrapper-classes)

13. Loogiset operaatiot 13.1

Anna Kuikka Pyöräkatu 9 B Kuopio GSM: Opiskelijanro: 60219K. Prioriteettijonot

Algoritmit 1. Luento 10 Ke Timo Männikkö

puuta tree hierarkkinen hierarchical

Ohjelmoinnin perusteet Y Python

Algoritmit 1. Luento 8 Ke Timo Männikkö

Algoritmit 2. Luento 9 Ti Timo Männikkö

Pikalajittelu: valitaan ns. pivot-alkio esim. pivot = oikeanpuoleisin

58131 Tietorakenteet Erilliskoe , ratkaisuja (Jyrki Kivinen)

Olio-ohjelmointi Syntaksikokoelma

13 Lyhimmät painotetut polut

Sekvenssi: kokoelma peräkkäisiä alkioita (lineaarinen

Algoritmit 1. Demot Timo Männikkö

Algoritmit 1. Luento 2 Ke Timo Männikkö

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

11. Javan toistorakenteet 11.1

B + -puut. Kerttu Pollari-Malmi

12. Javan toistorakenteet 12.1

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Listarakenne (ArrayList-luokka)

Transkriptio:

5. Prioriteettijonot Esimerkkinä prioriteetin käytöstä voisi olla lentoasemalle saapuva matkustaja, joka haluaa saada lentolipun ensimmäiselle Timbuktuun menevälle lennolle. Lentokone oli kuitenkin varattu täyteen etukäteen, mutta yleensä peruutuksia esiintyy, jolloin ennen lentoa voi saada peruutuspaikan. Kyseinen matkustaja tuli siis jonottamaan paikkaa, mutta myöhemmin tuleva saattaakin saada paikan ennen häntä, koska tällä myöhäisemmällä oli korkeampi prioriteetti, esim. aiemman ollessa alennetulla lipun hinnalla matkustava ja myöhemmän liikemieshintaan. 5. luku 236

Prioriteettijono tai etuoikeusjono (priority queue) on abstrakti tietotyyppi priorisoitujen alkioiden kokoelman tallettamista varten. Se tukee alkion lisäystä mielivaltaisesti valittuun kohtaan rakenteessa, mutta ainoastaan sellaisen alkion poistamista, jonka prioriteetti on järjestyksessä ensin. Tämä abstrakti tietotyyppi on täten täysin erilainen kuin tähän mennessä esitetyt pinot, jonot, kaksiloppuiset jonot, sekvenssit ja myös puut. Nämä perustuvat alkioiden tallettamiseen määrättyihin paikkoihin, jotka ovat yleensä lineaarisessa järjestyksessä ja tätä järjestystä käyttävät lisäys- ja poistotoiminnot. Prioriteettijonon yhteydessä ei ole ollenkaan sellaista paikkakäsitettä, vaan se tallettaa alkiot prioriteetin mukaan. Seuraavassa esitellään aluksi prioriteettijonon abstrakti tietotyyppi, sitten käsitellään sekvenssipohjaista toteutusta ja lopuksi binääripuuperusteista toteutusta, kekoa tai kasaa (heap), joka johtaa myös tehokkaaseen lajittelumenetelmään. 5. luku 237

5.1. Prioriteettijonon abstrakti tietotyyppi Tässä luvussa esitetään prioriteettijonon käsite avaimeen ja vertaimeen perustuen. Määritellään myös sen metodit. Avaimet, prioriteetit ja täydellisen järjestyksen relaatiot Tyypillisiä ovat tehtävät, joissa pitää laskea olioiden joukoille jotakin perustuen johonkin näille olioille määriteltyyn parametriin tai muuttujaan. Tällainen parametri on alkion avain (key) tai vertailuavain. Sen ei tarvitse välttämättä olla yksikäsitteinen sovelluksesta riippuen. Esim. yrityksiä voitaisiin verrata mm. työntekijöiden määrillä tai näiden palkkojen mukaan. Em. lentomatkustajan kohdalla prioriteetti voisi käsittää useita seikkoja, kuten usein lentävän kanta-asiakkaan edut, lipun hinnan ja matkustajan sisäänkirjausajan. 5. luku 238

Avain-käsite on luonteeltaan sangen yleinen eikä aina edes suoraan olion jokin ominaisuus, vaan ehkä sovelluksen asettama arvo, esim. lentomatkustajalle virkailijan antama prioriteetti. Kaikesta huolimatta pitää olla määriteltynä menettely, kuinka verrataan avaimia toisiinsa. Prioriteettijonon vertailut vaativat ristiriidattoman säännön. Jotta vertailusääntö, jota merkitään nyt symbolilla, olisi voimakas tässä mielessä, sen tulee olla täydellisen järjestyksen (total order) relaatio, ts. se on määritelty jokaiselle avainparille ja sen pitää toteuttaa ominaisuudet: Refleksiivisyysominaisuus (reflexive property): k k. Antisymmetrisyysominaisuus (antisymmetric property): jos k 1 k 2 ja k 2 k 1, niin k 1 = k 2. Transitiivisyysominaisuus (transitive property): jos k 1 k 2 ja k 2 k 3, niin k 1 k 3. 5. luku 239

Mikä tahansa sääntö, joka toteuttaa nämä ominaisuudet, ei koskaan johda ristiriitaan vertailussa. Sääntö määrittelee itse asiassa lineaarisen järjestyksen avainten joukossa. Jos alkiokokoelmalle on määritelty täydellinen järjestys, pienimmän avaimen käsite, k min, on hyvin määritelty, jolloin k min < k kaikille kokoelman avaimille k. Prioriteettijono P on alkioiden säiliö, jossa kullakin on avaimensa. Prioriteetti-nimitys tulee siitä, että avaimet määräävät prioriteetin, jolla alkioita haetaan ja poistetaan. Kyseeseen tulevat kaksi perusmetodia: insertitem(k,e): lisää alkion e avainta k käyttäen prioriteettijonoon P. removeminelement(): palauttaa ja poistaa prioriteettijonosta P alkion, jonka avain on pienin (pienempi tai yhtä suuri kuin minkä tahansa muun alkion avain rakenteessa P). 5. luku 240

Esim. 5.1. Tarkastellaan jälleen lentomatkustajan tilannetta. Mahdollisten peruutusten tähden lentoyhtiö ylläpitää prioriteettijonoa niille hieman ennen lentoa ilmestyville matkustajille, joille ei heti järjestynyt paikkaa lentokoneen täyttymisen vuoksi. Jokaisen matkustajan prioriteetti määrätään lipun hinnan, matkustajan statuksen ja sisäänkirjausajan mukaan. Viittaus matkustajaan asetetaan prioriteettijonoon hänelle annetun prioriteetin mukaan. Kun jokin paikka vapautuu peruutuksen vuoksi, prioriteettijonosta otetaan pois prioriteetiltaan ensimmäisenä oleva, jolle paikka annetaan. Jonoa päivitetään tilanteen mukaan niin kauan, kunnes sisäänkirjaus lopetetaan vähän ennen lentoa tai jono tyhjenee. 5. luku 241

Lajittelu prioriteettijonoa käyttäen Muuan tärkeä prioriteettijonon sovellus on lajittelu. Siinä on sekvenssi S ja n alkiota, joita verrataan toisiinsa täydellisen järjestyksen mielessä ja järjestetään uudelleen kasvavaan järjestykseen (ei-vähenevään ainakin). Algoritmi on varsin yksinkertainen, ja siinä on sekvenssin S lisäksi prioriteettijono P. Vaiheita on kaksi: 1. Ensiksi sekvenssin S alkiot asetetaan alunperin tyhjään prioriteettijonoon P operaation insertitem n perättäisellä käytöllä, yksi kullekin alkiolle. 2. Toiseksi otetaan olioita P:stä kasvavassa eli ei-vähenevässä järjestyksessä operaation removeminelement n kutsulla ja asetetaan alkiot takaisin järjestykseen sekvenssiin S. 5. luku 242

Koodissa 5.1. on esitetty algoritmin luonnos prioriteettijonoa käyttävää lajittelua varten. Varsinainen algoritmi se ei ole, koska siinä ei ole puututtu operaatioiden insertitem ja removeminelement toteutuksiin. Menetelmän kompleksisuuskin riippuu niistä, joten niiden ollessa vielä ilman täsmällistä määrittelyä koko menetelmän tehokkuus jää toistaiseksi auki. Menetelmä on toisaalta yleinen. Se toimii mille tahansa prioriteettijonolle P. Tämä algoritmin luonnos on paradigma usealle suositulle algoritmille, nimittäin valintalajittelu, lisäyslajittelu ja kekolajittelu, joita puidaan tuonnempana. 5. luku 243

Algorithm PriorityQueueSort(S,P): Input: Sekvenssi S, jossa on n alkiota ja näille on määritelty täydellisen järjestyksen relaatio, sekä prioriteettijono P, joka vertaa avaimia soveltaen samaa järjestysrelaatiota. Output: S lajiteltuna järjestysrelaation mukaisesti. while S ei ole tyhjä do e S.removeFirst() { poista alkio e sekvenssistä S } P.insertItem(e,e) { avain on tässä samalla itse alkio } while P ei ole tyhjä do e P.removeMinElement() { poista pienin alkio P:sta } S.insertLast(e) { lisää alkio S:n loppuun } Koodi 5.1. Luonnos algoritmiksi lajittelua varten sovellettaessa prioriteettijonoa. 5. luku 244

Prioriteettijonon metodit Kun on kuvailtu intuitiivisesti prioriteettijonon abstrakti tietotyyppi, voidaan nyt perehtyä siihen yksityiskohtaisesti. Tarvitaan oheiset metodit: size(): Palauttaa prioriteettijonon P alkioiden lukumäärän. Tulos: kokonaisluku isempty(): Testaa, onko P tyhjä. Tulos: totuusarvo insertitem(k,e): Lisää uuden alkion e avaimella k P:hen. Syöte: Oliot k (avain) ja e (alkio). minelement(): Palauttaa (mutta ei poista) P:n alkion, jolla on pienin avain. Virhe esiintyy prioriteettijonon ollessa tyhjä. Tulos: olio (alkio) 5. luku 245

minkey(): Palauttaa P:n pienimmän avaimen. Virhe esiintyy prioriteettijonon ollessa tyhjä. Tulos: olio (avain) removeminelement(): Palauttaa ja poistaa P:stä alkion, jolla on pienin avain. Virhe esiintyy prioriteettijonon ollessa tyhjä. Tulos: olio (alkio) Kuten havaittiin, prioriteettijono on käsitteellisesti hyvin yksinkertainen, koska lisäykset ja poistot tehdään ainoastaan avaimen perusteella, kun taas sekvenssin tapauksessa pitää olla useita erilaisia metodeja lisäyksiä ja poistoja varten käytettäessä eri toteutuksia. Pohditaan vielä esimerkin kautta prioriteettijonon olemusta. Esim. 5.2. Seuraava taulukko esittää operaatioiden sarjan ja näiden vaikutuksen alunperin tyhjään prioriteettijonoon P, jossa alkio e ja tämän avain k osoitetaan parilla (k,e). 5. luku 246

Sarake P:n sisältö on lähinnä viitteellinen, koska siinä alkiot on järjestetty avaintensa mukaan, mutta todellisuudessa kulloinkin on välittömästi saatavissa vain minimiavaimen alkio ja parit talletetaan toteutuksesta riippuen. operaatio tulos P:n sisältö insertitem(5,a) - {(5,A)} insertitem(9,c) - { (5,A),(9,C) } insertitem(3,b) - { (3,B),(5,A),(9,C) } insertitem(7,d) - { (3,B),(5,A),(7,D),(9,C) } minelement() B { (3,B),(5,A),(7,D),(9,C) } minkey() 3 { (3,B),(5,A),(7,D),(9,C) } removeminelement() B { (5,A),(7,D),(9,C) } size() 3 { (5,A),(7,D),(9,C) } removeminelement() A { (7,D),(9,C) } 5. luku 247

removeminelement() D { (9,C) } removeminelement() C { } removeminelement() virhe { } isempty tosi { } Jää vielä mietittäviksi kaksi keskeistä seikkaa: Miten pidetään tietoa yllä avainten ja alkioiden välisistä yhteyksistä? Miten verrataan avaimia pienimmän avaimen määräämiseksi? Näihin vastauksen saamiseksi pohditaan kahta mielenkiintoista suunnittelumallia. 5. luku 248

Kompositiot ja vertaimet Prioriteettijono käyttää kahta suunnittelumallia, kompositioita ja vertaimia, joita tarkastellaan seuraavaksi. Kompositio Kompositiomallissa (composition pattern) määritellään yksittäinen olio e, joka on muiden olioiden kompositio. Tätä mallia hyödynnetään määriteltäessä oliot, jotka on määrä tallettaa prioriteettijonoon pareina. Pari (pair) (k,e) on yksinkertaisin kompositio, sillä se yhdistää kaksi oliota yhdeksi oliopariksi. Tämän toteuttamiseksi määritellään luokka, joka tallettaa kaksi oliota muuttujiinsa ja joka antaa muuttujien saantija päivitysmetodit. Koodissa 5.2. on tällainen toteutus avainalkio-pareille. Muut kompositiot voivat olla kolmikkoja, joissa on kolme oliota, nelikköjä vastaavasti tai yleisiä kompositioita, joissa on mielivaltaisesti valittu määrä olioita (esim. sekvenssissä). 5. luku 249

public class Item { private Object key, elem; protected Item ( Object k, Object e) { key = k; elem = e; } public Object key() { return key; } public Object element() { return elem; } public void setkey(object k) { key = k; } public void setelement(object e) { elem = e } } Koodi 5.2. Avain-alkio-parien tallettamista varten luokka prioriteettijonolle. Kompositio-oliot voisivat puolestaan sisältää muita kompositioolioita, jolloin syntyisi puumainen hierarkia, mutta näihin ei tässä puututa. 5. luku 250

Vertain On määriteltävä, kuinka avaimia verrataan toisiinsa. Jos lähdettäisiin toteuttamaan kullekin avaintyypille oma prioriteettijononsa ja tälle omat vertailuoperaationsa, menettely ei olisi kovinkaan yleinen ja olisi paljon melko samankaltaista koodia. Parempi ratkaisu on tehdä yleinen prioriteettijonoluokka, joka tallettaa avainluokan ilmentymät toteuttaen soveliaan vertailurajapinnan ja kapseloiden tarpeelliset, tavanomaiset vertailumetodit. Tällöin on mahdollista tehdä yksittäinen prioriteettijono, joka voi käsitellä useita erityyppisiä avaimia. Toisinaan tässä on kuitenkin vaadittu liikaa avaimilta, sillä on avaimia, joista ei välittömästi tiedä, kuinka niitä pitäisi verrata oheisen esimerkin tapaan. Esim. 5.3. Olkoon annettu avaimet 4 ja 11. Kun nämä ovat tavanomaiseen tapaan verrattavissa olevia kokonaislukuja, on 4 < 11. Jos ne ovat merkkijonoja, on 11 < 4 vertailun ollessa leksikograafinen. 5. luku 251

Päästäksemme yleiseen ja uudelleenkäytettävään ratkaisuun prioriteettijonolle ei pidä luottaa vain avaimiin ja niiden vartailusääntöihin. Sen sijaan käytetään erityisiä vertainolioita, jotka on ulkoistettu avaimiin nähden. Vertain on olio, joka vertaa kahta avainta. Oletetaan, että prioriteettijonolle P on toteutettu vertain, joka on päivitettävissä tarvittaessa. Prioriteettijono P käyttää vertainta verratakseen kahta avainta. Vertainolio käsittää seuraavat metodit: islessthan(a,b): Tosi, jos ja vain jos a on pienempi kuin b. Syöte: olioiden pari Tulos: totuusarvo islessthanorequal(a,b): Tosi, jos ja vain jos a on pienempi tai yhtä suuri kuin b. Syöte: olioiden pari Tulos: totuusarvo isequalto(a,b): Tosi, jos ja vain jos a on yhtä suuri kuin b. Syöte: olioiden pari Tulos: totuusarvo 5. luku 252

isgreaterthan (a,b): Tosi, jos ja vain jos a on suurempi kuin b. Syöte: olioiden pari Tulos: totuusarvo isgreaterthanorequal (a,b): Tosi, jos ja vain jos a on suurempi tai yhtä suuri kuin b. Syöte: olioiden pari Tulos: totuusarvo iscomparable(a): Tosi, jos ja vain jos a on vertailtavissa. Syöte: olio Tulos: totuusarvo Vertain tarjoaa yleisen ja uudelleenkäytettävän tavan verrata olioita toisiinsa. Saadaan geneerinen tehokas väline prioriteettijonon rakentamista varten, josta voidaan tarvittaessa löytää tai poistaa minimialkio. Lisäksi käsittely voidaan ikään kuin kääntää määrittelemällä vertaimessa mm. operaatioiden sijasta operaatiot. Tällöin voidaan saada ja poistaa maksimialkio minimin sijasta, kun prioriteettijono on vastaavasti rakennettu käänteisenä. 5. luku 253

5.2. Prioriteettijonon toteutus sekvenssiä käyttäen Tässä luvussa esitetään prioriteettijonon sekvenssiä alkioiden tallettamiseen käyttävä toteutus. Annetaan kaksi vaihtoehtoista toteutustapaa sen mukaan, pidetäänkö sekvenssiä lajiteltuna vai ei. Toteutus lajittelemattomalla sekvenssillä Talletetaan alkiot prioriteettijonoon P ja niiden avaimet sekvenssiin S. S:n alkiot ovat siis pareja (k,e), missä e on alkio ja k sen avain. Yksinkertainen tapa toteuttaa metodi insertitem(k,e) on lisätä uusi olio p =(k,e) sekvenssin S loppuun suorittamalla metodi insertlast(p) siinä. Tämä operaatio vaatii O(1) aikaa riippumatta siitä, onko sekvenssi toteutettu taulukkona tai linkitettynä listana (luku 3.5.). 5. luku 254

Näin lisäämällä ei oteta huomioon avainten järjestystä. Niinpä operaatioiden minelement, minkey ja removeminelement suorittaminen edellyttää sekvenssin S kaikkien alkioiden läpikäyntiä olion p = (k,e) hakemiseksi, missä k on minimi. Sekvenssin toteutustavasta riippumatta tämä tarvitsee ajan O(n), missä n on olioiden määrä P:ssä tuolla hetkellä. Nämä metodit vaativat myös (n) pahimmassa tapauksessa, koska pitää etsiä kaikki alkiot läpi. Näistä tulee niin ikään (n). Näin ollen lisäys on suoritettavissa vakioajassa, mutta operaatio removeminelement tarvitsee lineaarisen ajan. Huomattakoon, että käytännön tilanteessa tulos on varsin kelvollinen, jos avainten määrä on pieni. Toteutus käyttäen lajiteltua sekvenssiä Luonnollinen vaihtoehto edelliselle on käyttää prioriteettijonoa P sekvenssin S muodossa, missä S:n sisältö eli avaimet on lajiteltuna. Sekvenssin ensimmäinen avain on pienin. 5. luku 255

Metodit minelement ja minkey voidaan siis toteuttaa hakemalla sekvenssin ensimmäinen avain S:n metodilla first. Samoin metodi prioriteettijonon P removeminelement toteutetaan S:n metodilla remove metodin first avulla saadusta paikasta. Jos S on toteutettu taulukon tai linkitetyn listan avulla ja ensimmäisen alkion poisto (luku 3.5.) on mahdollista vakioajassa siinä, niin prioriteettijonon P minimin löytäminen ja poisto tapahtuu ajassa O(1). Tämä on näin ollen nopeampaa kuin lajittelemattomassa versiossa. Hyötyä ei ole kuitenkaan kaikille metodeille, sillä nyt P:n metodi insertitem vaatii sekvenssin S selaamisen kokonaisuudessaan pahimmassa tapauksessa, jotta oikea lisäyskohta löydetään avainten ollessa lajiteltuina. Täten tämän suoritusaika on O(n), kun n on alkioiden määrä P:ssä lisäystä tehtäessä. Taulukko 5.1. esittää aikakompleksisuudet molempien lähestymistapojen osalta. Edellinen sallii nopean lisäyksen, mutta hitaan kyselyn ja poiston. Jälkimmäinen antaa vastakkaisen tilanteen. 5. luku 256

Taulukko 5.1. Prioriteettijonojen metodien suoritusaikojen vertailu, kun sekvenssi on lajittelematon tai lajiteltu. Sekvenssi on toteutettu taulukkona tai kahteen suuntaan linkitettynä listana. Alkioita olkoon n prioriteettijonossa metodia suoritettaessa. Tilavaatimus on O(n), kun rakenne on toteutettu listana, mutta O(N), jos taulukkona, missä N n. 5. luku 257

Valintalajittelu ja lisäyslajittelu Tämän 5. luvun alussa mainittiin prioriteettijonoon perustuva lajittelu. Siinä prioriteettijonon sisältö lajiteltiin kahdessa vaiheessa, ensin lisättiin alkio alkiolta ja toiseksi poistettiin alkioittain operaatiolla removeminelement. Nyt tarkastellaan tästä kahta variaatiota. Valintalajittelu Kun prioriteettijono P toteutetaan lajittelemattomana sekvenssinä, ensimmäinen mainituista vaiheista vaatii ajan O(n), sillä jokainen alkio lisätään vakioajassa. Toisessa vaiheessa - edellyttäen kahden avaimen vertaamisen vievän vakioajan - jokaisen operaation removeminelement suoritus kestää suhteessa P:n senhetkiseen alkioiden määrään. 5. luku 258

Niin muodoin laskennan pullonkaula esiintyy tämän toistetun minimialkion valinnan toteutuksessa lajittelemattomasta sekvenssistä (kuva 5.1.) Tästä johtuen algoritmia kutsutaan valintalajitteluksi (selection-sort). sekvenssi S prioriteettijono P syöte (7,4,8,2,5,3,9) () 1. vaihe (a) (4,8,2,5,3,9) (7) (b) (8,2,5,3,9) (7,4) (g) () (7,4,8,2,5,3,9) Kuva 5.1. (alkuosa) Valintalajittelusta esimerkki, jossa algoritmi on esitetty kaksivaiheisena. Ensimmäisessä vaiheessa poistetaan alkio kerrallaan sekvenssistä ja lisätään se prioriteettijonoon (ensimmäisen vaiheen jälkeen P on itse asiassa kopio lähtötilanteen S:stä). 5. luku 259

2. vaihe (a) (2) (7,4,8,5,3,9) (b) (2,3) (7,4,8,5,9) (c) (2,3,4) (7,8,5,9) (d) (2,3,4,5) (7,8,9) (e) (2,3,4,5,7) (8,9) (f) (2,3,4,5,7,8) (9) (g) (2,3,4,5,7,8,9) () Kuva 5.1. (loppuosa) Toisessa vaiheessa suoritetaan toistuvasti removeminelement P:lle (selataan joka kerta P) ja lisätään palautetut alkiot sekvenssin S loppuun. 5. luku 260

Analysoidaan nyt valintalajittelua. Kuten sanottu, ongelmana on toinen vaihe, jossa toistuvasti poistetaan pienimmän avaimen alkio prioriteettijonosta P. Aluksi tämän koko on n ja vähenee yhdellä kullakin operaation removeminelement suorituksella. Näin ollen ensimmäinen suorituskerta vaatii O(n), toinen O(n-1) jne., kunnes viimeisellä (n:s) vaatii ajan O(1). Kokonaisaika toiselle vaiheelle on täten Tästä saadaan Toinen vaihe tarvitsee siis O(n 2 ) ja samoin koko algoritmi. 5. luku 261

Lisäyslajittelu Jos prioriteettijono P toteutetaan lajitellulla sekvenssillä, toisen vaiheen suoritusaikaa parannetaan lineaariseksi, O(n), kunkin operaation removeminelement tapauksen tarvitessa O(1). Epäonneksi ensimmäinen vaihe tulee nyt pullonkaulaksi. Pahimmassa tapauksessa jokaisen operaation insertitem suorituskerta on suhteessa alkioiden määrään kyseisellä hetkellä prioriteettijonossa. Tämä määrä on aluksi nolla ja kasvaa n:ään. Näin ensimmäinen lisäys vaatii O(1), toinen O(2) jne., kunnes viimeinen (n:s) vaatii O(n) pahimmassa tapauksessa. Tämä lajittelumenetelmä (kuva 5.2.) on nimeltään lisäyslajittelu (insertion-sort). Lisäyslajittelun ensimmäisen vaiheen analyysi antaa samalla tavalla kuin valintalajittelun toinen vaihe pahimmassa tapauksessa O(n 2 ) ja lopuksi samoin koko lisäyslajittelu. Niinpä nämä ovat luonteeltaan samanlaisia yksinkertaisia, mutta asymptoottisesti melko tehottomia kuin kuplalajittelu. 5. luku 262

sekvenssi S prioriteettijono P syöte (7,4,8,2,5,3,9) () 1. vaihe (a) (4,8,2,5,3,9) (7) (b) (8,2,5,3,9) (4,7) (c) (2,5,3,9) (4,7,8) (d) (5,3,9) (2,4,7,8) (e) (3,9) (2,4,5,7,8) (f) (9) (2,3,4,5,7,8) (g) () (2,3,4,5,7,8,9) Kuva 5.2. (alkuosa) Kaavamainen esitys lisäyslajittelusta, jossa algoritmi noudattaa kaksivaiheista menettelyä sekvenssinä toteutetulle prioriteettijonolle P. Ensimmäisessä vaiheessa poistetaan toistuvasti ensimmäinen alkio S:stä ja lisätään se P:hen selaamalla P, kunnes löydetään oikea paikka alkiolle. 5. luku 263

2. vaihe (a) (2) (3,4,5,7,8,9) (b) (2,3) (4,5,7,8,9)... (g) (2,3,4,5,7,8,9) () Kuva 5.2. (loppuosa) Toisessa vaiheessa toistuvasti suoritetaan operaatiota removeminelement P:lle. Kustakin saadaan ensimmäinen alkio, joka lisätään sekvenssin S loppuun. Kuvatuista lähestymistavoista valintalajittelu antoi nopean ensimmäisen vaiheen prioriteettijonolajittelun mielessä, kun taas sen toinen vaihe oli hidas. Lisäyslajittelulla tilanne oli päinvastainen. Jos voitaisiin jotenkin tasapainottaa näiden vaiheiden suoritusaikoja, voitaisiin samalla nopeuttaa kokonaissuoritusaikaa. Tästä on kysymys seuraavassa luvussa. 5. luku 264

5.3. Keot Prioriteettijono voidaan toteuttaa tehokkaasti rakenteella nimeltä keko tai kasa (heap). Tässä sekä lisäys että poisto pystytään tekemään logaritmisessa ajassa, joka on huomattava parannus edeltäviin sekvenssipohjaisiin toteutuksiin. Keossa talletetaan alkiot määrätyntyyppiseen binääripuurakenteeseen, joka on tehokkuuden lähtökohta. Kekotietorakenne Keko (kuva 5.3.) on binääripuu T, joka tallettaa kokoelman avaimia solmuihinsa ja täyttää kaksi ehtoa: relaatio, joka on määritelty sen suhteen, miten avaimet on talletettu puuhun T, ja rakenne, joka on määritelty puun T solmujen suhteen. Avainten täydellisen järjestyksen relaatio on esim. vertaimella tuotettu. Tässä määritelmässä keon lehtiinkin on talletettu avaimia ( paikanpitäjilehdistä on nyt luovuttu). 5. luku 265

4 5 6 15 9 7 20 16 25 14 12 11 8 Kuva 5.3. Esimerkki keosta, jossa on 13 kokonaislukuavainta ja viimeisen solmun avain on 8. Huomaa, että nyt on paikanpitäjälehdistä luovuttu. 5. luku 266

Keon T avaintenvälinen relaatio on seuraava: Kekojärjestysominaisuus (heap-order property): Keon T jokaisella solmulla v juurta lukuunottamatta solmuun talletettu avain on suurempi tai yhtä suuri kuin avain, joka on talletettu solmun v vanhempaan. Tästä ominaisuudesta johtuen (käytetään myös nimitystä osittainen järjestys, koska avaimilla saman tason sisällä ei ole keskinäistä järjestystä määritelty) polulla juuresta lehteen kohdattavat avaimet ovat kasvavassa eli ei-vähenevässä järjestyksessä. Minimiavain on aina juuressa. Tämä onkin tärkein avain ja on keon huipulla. (Tätä kekorakennetta ei pidä sekoittaa 3. luvussa mainittuun Javan muistinhallinnan heapkäsitteeseen.) 5. luku 267

Jos määritellään käytettävä vertain vastakkaisella tavalla kuin tavallisesti (kuten luvun 5.1. lopussa esitettiin), saadaan juureen maksimiavain ja avaimet pienenevät lehtiä kohti siirryttäessä. Jatkossa pitäydytään kuitenkin tavanomaisessa lähestymistavassa, jossa juuri käsittää minimiavaimen alkion. Tehokkuuden takia halutaan keon olevan mahdollisimman pieni, so. matala. Tämä saadaan aikaan vaatimalla, että keon T tulee olla täydellinen (complete). Kun binääripuun T taso i sisältää syvyydellä i olevat solmut, tämä rakenne voidaan määritellä näin: Täydellinen binääripuu (complete binary tree): Binääripuu T, jonka korkeus on h, on täydellinen, jos tasoilla 0, 1, 2,, h-1 on maksimimäärä solmuja (tasolla i, 0 i h-1, on 2 i solmua) ja tasolla h-1 kaikki sisäsolmut ovat lehtien vasemmalla puolella. 5. luku 268

Kuvan 5.3. puu edeltä on täydellinen. Sillä, että sisäsolmut tasolla h-1 ovat lehtien vasemmalla puolella, tarkoitetaan tavanomaista tapaa, jolla binääripuut kuvataan. Tällöin nuo sisäsolmut käydään läpi ensiksi - esim. kuljettaessa välijärjestyksessä - ja vasta sitten saman tason lehdet. Tällöin voidaan määritellä myös keon viimeinen solmu, joka on keon T oikeanpuolimmainen, syvin solmu. Siitä, että T on täydellinen, seuraa keskeinen ominaisuus oheisen lauseen mukaisesti. Lause 5.1. Keko T, jossa on n solmua, on korkeudeltaan h = log n. Perustelu: Kun T on täydellinen, sen solmujen lukumäärä on vähintään 1+2+4+ +2 h-1 +1=2 h -1+1=2 h. 5. luku 269

Tämä alaraja saadaan, kun on olemassa ainoastaan yksi solmu tasolla h. Vastaavasti saadaan keon T ollessa täydellinen yläraja solmujen lukumäärälle eli 1+2+4+ +2 h =2 h+1-1. Tämä yläraja tulee tasolla h ollessa täydet 2 h solmua. Koska solmujen määrä on yhtä kuin avainten määrä n, saadaan 2 h n ja n 2 h+1-1. Ottamalla logaritmit molemmin puolin epäyhtälöistä saadaan log(n+1)-1 h log n, joka merkitsee, että h = log n. 5. luku 270

Lauseella 5.1. on mitä tärkein seuraus. Jos päivitysoperaatiot voidaan tehdä keossa ajassa, joka on suhteessa sen korkeuteen, niin nuo operaatiot toimivat logaritmisessa ajassa. Täten prioriteettijonon toteuttaminen kekona on hyvin tehokasta. Prioriteettijonon toteuttaminen kekona Esitetään prioriteettijonon abstraktin tietotyypin toteutus kekona (kuva 5.4. ja koodi 5.3.). Se käsittää keon T, jota merkitään merkinnällä k(v) ja jossa ovat solmuissa talletettuina prioriteettijonon alkiot ja avaimet (koodi 5.2. edeltä), viittauksen keon T viimeisen solmun paikkaan ja vertaimen, joka määrittelee avainten täydellisen järjestyksen relaation. 5. luku 271

keko viimeinen vertain (4,C) (5,A) (6,Z) < = > (15,K) (9,F) (7,Q) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) Kuva 5.4. Kokonaislukuavainten ja merkkialkioiden prioriteettijono toteutettuna kekona. Solmut käsittävät avain-alkio-parin. Keon rakentuminen perustuu ainoastaan avaimiin. 5. luku 272

public class HeapSimplePriorityQueue implements SimplePriorityQueue BinaryTree T; Position last; Comparator comparator; Koodi 5.3. Keon toteuttamisesta. Oletetaan keko toteutetun binääripuun rakenteilla (luku 4.4.), joko linkitettynä rakenteena tai sekvenssinä. Lisäksi alkiot on talletettu binääripuuhun avain-alkio-pareina, kuten koodissa 5.2. esitettiin. Koska T on täydellinen binääripuu, kekoon T liittyvä sekvenssi sekvenssiperusteisen toteutuksen tapauksessa käsittää n alkiota eikä siinä ole yhtään tyhjiä solmuja. 5. luku 273

Lisäys Tarkastellaan metodin insertitem suorittamista keossa T. Tämä on esitetty kuvassa 5.5. Uuden avain-alkio-parin (k,e) lisäämiseksi kekoon T pitää lisätä uusi solmu kekoon. Jotta T pysyisi täydellisenä binääripuuna, uusi solmu on lisättävä uudeksi viimeiseksi solmuksi. Tätä varten pitää ensin etsiä oikea paikka z, jolle voidaan lisätä uusi alkio solmuun z samalla säilyttäen keon T täydellisenä (kuva 5.5.(a)-(b)). Solmua z kutsutaan lisäyspaikaksi (insertion position). 5. luku 274

(4,C) (4,C) (5,A) (6,Z) (5,A) (6,Z) (15,K) (9,F) (7,Q) (20,B) (15,K) (9,F) (7,Q) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (2,T) z (a) (b) Kuva 5.5. (alku) Uuden alkion lisääminen kuvan 5.4. kekoon avaimen ollessa 2: (a) alkutilanne, (b) uuden alkion lisääminen vanhan viimeisen solmun oikealle puolelle. (Kun paikanpitäjälehtiä ei käytetä, binääripuu ei aina ole täysin aito, kuten tässä lisäyspaikassa). 5. luku 275

Tavallisesti solmu z on valittömästi viimeisen solmun, lehti w, oikealla puolella (kuva 5.6.(b)). On silti kaksi erikoistapausta, joissa tämä sääntö ei päde: Jos nykyinen viimeinen solmu w on määritelty ja on oikeanpuolimmainen tasollaan, niin z on alimman tason vasemmanpuolimmainen (kuva 5.6.(a)). Jos keolla T ei ole solmuja (prioriteettijono on tyhjä ja keon viimeistä solmua ei ole määritelty), niin z on keon T juuri. Selvitetään hiukan myöhemmin, miten löydetään lisäyspaikka z lähdettäessä viimeisestä solmusta w. Joka tapauksessa lisäysoperaation parille (k,e) suorittamisen jälkeen solmusta z tulee uusi viimeinen solmu ja siihen talletetaan uusi avain-alkiopari (k,e), jolloin k(z)=k. 5. luku 276

Sitten puu T on täydellinen, mutta se voi rikkoa kekojärjestysominaisuuden. Jollei solmu z ole juuri (prioriteettijono oli tyhjä ennen lisäystä), verrataan avainta k(z) avaimeen k(u), joka on talletettu solmun z vanhempaan u. Jos on k(u) > k(z), niin pitää korjata kekojärjestysominaisuus vallitsevaksi, mikä tehdään lokaalisesti vaihtamalla avain-alkio-parit solmujen z ja u välillä (kuva 5.5.(c)-(d)). Edellinen vaihto aiheuttaa uuden avain-alkio-parin (k,e) siirtymisen yhden tason verran ylöspäin. Vieläkään kekojärjestysominaisuus ei ole voimassa, joten vaihtamista on jatkettava ylöspäin, kunnes ominaisuus on voimassa (kuva 5.5.(e)-(h)). 5. luku 277

(4,C) (4,C) (5,A) (6,Z) (5,A) (6,Z) (15,K) (9,F) (7,Q) (15,K) (9,F) (7,Q) (2,T) (20,B) (2,T) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (20,B) (c) (d) Kuva 5.5. (jatkoa) Uuden alkion lisääminen kuvan 5.4. kekoon avaimen ollessa 2: (c) vaihto, (d) vaihdon jälkeen. 5. luku 278

(4,C) (4,C) (5,A) (2,T) (5,A) (2,T) (15,K) (9,F) (7,Q) (6,Z) (15,K) (9,F) (7,Q) (6,Z) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (20,B) (e) (f) Kuva 5.5. (jatkoa) Uuden alkion lisääminen kuvan 5.4. kekoon avaimen ollessa 2: (e) toinen vaihto, (f) vaihdon jälkeen. 5. luku 279

(2,T) (2,T) (5,A) (4,C) (5,A) (4,C) (15,K) (9,F) (7,Q) (6,Z) (15,K) (9,F) (7,Q) (6,Z) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (20,B) (g) (h) Kuva 5.5. (jatkoa) Uuden alkion lisääminen kuvan 5.4. kekoon avaimen ollessa 2: (g) kolmas eli viimeinen vaihto, (h) vaihdon jälkeen. 5. luku 280

Vaihtoja ylöspäin kutsutaan keon ylöskuplimiseksi (up-heap bubbling). Vaihto joko korjaa kekojärjestysominaisuuden vallitsevaksi tai siirtää säännön rikkovan kohdan taso kerrallaan ylemmäksi. Pahimmassa tapauksessa ylöskupliminen vie avainalkio-parin koko polun lehdestä juureen asti, kuten kuvassa 5.5. Täten pahimman tapauksen suoritusaika metodille insertitem on suhteessa puun T korkeuteen, so. O(log n), koska T on täydellinen. Lisäyspaikan löytäminen Lisäysmetodin käyttämiseksi on ensiksi löydettävä lisäyspaikka z, johon solmu lisätään ja talletetaan uusi avain-alkio-pari. Jos keko T on toteutettu sekvenssinä (luku 4.4.), niin lisäyspaikan z löytäminen on suoraviivaista, koska z on solmu, johon on liitetty numero n+1 (ks. kuva 4.14. s. 220). Tämä toimii yleisesti ja myös em. kahdessa erikoistapauksessa. 5. luku 281

Kun T toteutetaan linkitettynä rakenteena (luku 4.4.) tai jos ei tiedettäisi toteutusta ja päästäisiin siihen käsiksi ainoastaan binääripuun abstraktin tietotyypin metodeilla, niin lisäyspaikan z löytäminen ei ole yhtä helppoa. Erikoistapauksessa, jossa T on aivan aluksi tyhjä, z on juuri. Muuten aloitetaan keon T nykyisestä viimeisestä solmusta w ja etsitään lisäyspaikka z seuraavaan tapaan (kuva 5.6.): 1. Aloittaen viimeisestä solmusta w mene ylös puuta toistuvasti kutsuen metodia parent, kunnes juuri tai vasen lapsi saavutetaan. (a) Jos on saavutettu juuri, olkoon se u. Tämä on erikoistapaus, jossa viimeinen solmu on tasonsa oikeanpuolimmainen solmu. (b) Muuten (vasen lapsi on saavutettu) olkoon u saavutetun solmun sisar. (Huom. Solmu w itse voi olla vasen lapsi, jolloin uusi solmu tulee suoraan sen oikeaksi sisareksi. Näin tulisi toinen lisäys kuvassa 5.6.(b).) 2. Aloittaen solmusta u mene alas puuta toistuvasti kutsuen metodia leftchild, kunnes paikka z saavutetaan. 5. luku 282

(2,T) (2,T) (5,A) (4,C) (5,A) (4,C) (15,K) (9,F) (7,Q) (6,Z) (15,K) (9,F) (7,Q) (6,Z) (16,X) (25,J) (14,E) (12,H) (11,S) (8,W) (20,B) (10,L) w (16,X) (25,J) (14,E) (12,H) w z z (a) (b) Kuva 5.6. Lisäyspaikan z löytäminen keosta: (a) siirtyminen ylöspäin, kunnes saavutetaan juuri, ja sitten siirtyminen alas sekä (b) siirtyminen ylöspäin, kunnes vasen lapsi saavutetaan, sitten siirtyminen sisarukseen ja lopulta siirtyminen alas vasemmalle. 5. luku 283

Jos T on toteutettu taulukkopohjaisena sekvenssinä, uusi viimeinen solmu on saatavissa suoraan ajassa O(1). Jos T on toteutettu linkitettynä rakenteena, z löydetään siirtymisellä ensin ylös ja toisella alas, mikä vaatii ajan O(log n). Koodissa 5.4. on metodin insertitem toteutus. public void insertitem(object k, Object e) throws InvalidKeyException { if (!comparator.iscomparable(k)) throw new InvalidKeyException( Invalid Key ); Position z; // insertion position if (isempty()) z = T.root(); else { Koodi 5.4. (alku) Metodi insertitem apumetodeineen. 5. luku 284

z = last; while (!T.isRoot(z) &&!isleftchild(z)) z = T.parent(z); if (!T.isRoot(z)) z = T.rightChild(T.parent(z)); while (!T.isExternal(z)) z = T.leftChild(z); } T.expandExternal(z); // inserting a leaf T.replace(z, new KeyElementPair(k,e)); last = z; Position u; Koodi 5.4. (jatkoa) Metodi insertitem apumetodeineen. 5. luku 285

while (!T.isRoot(z)) { // up-heap bubbling } } u = T.parent(z); if (comparator.islessthanorequalto(keyofposition(u), T.swap(u,z); z = u; private Object keyofposition(position p) throws InvalidPositionException { } keyofposition(z))) break; return ((KeyElementPair) p.element()).key(); //cast shouldn t fail Koodi 5.4. (jatkoa) Metodi insertitem apumetodeineen. 5. luku 286

private boolean isleftchild(position p) throws InvalidPositionException { } try { return T.leftChild(T.parent(p)).equals(p); } catch (boundaryviolationexception e) { } return false; // happens when p is the root Koodi 5.4. (loppu) Metodi insertitem apumetodeineen. Kääntämällä edellä kuvattu menettely uuden viimeisen solmun löytämiseksi lisäyksessä viimeinen solmu on päivitettävissä poiston jälkeen, jota pohditaan seuraavaksi. 5. luku 287

Poisto Silmäillään nyt metodia removeminelement. Tätä havainnollistaa kuva 5.7. Pienimmän avaimen alkio on tunnetusti juuressa r. Jos se ei kuitenkaan ole ainoa keon T solmu, ei voida poistaa yksinkertaisesti solmua r, koska tämä rikkoisi binääripuurakenteen. Sen sijaan haetaan viimeinen solmu w, siirretään sen avain-alkio-pari juureen r ja sitten poistetaan viimeinen solmu. Operaatiossa poistetaan solmu w (kuva 5.7.(a)-(b)). Tämän jälkeen viittaus viimeiseen solmuun on päivitettävä, mikä tehdään käänteisesti edellä kuvattuun, lisäyksen yhteydessä nähtyyn. 5. luku 288

(4,C) (13,W) (13,W) (5,A) (6,Z) (5,A) (6,Z) (15,K) (9,F) (7,Q) (20,B) (15,K) (9,F) (7,Q) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) w (16,X) (25,J) (14,E) (12,H) (11,S) (a) (b) Kuva 5.7. (alku) Pienimmän avaimen alkion poistaminen keosta: (a) viimeisen solmun poiston aloittaminen siirtämällä avain-alkio-pari juureen ja (b) poiston alkuvaiheen jälkeen. 5. luku 289

Vaikka T on nyt täydellinen binääripuu, se saattaa rikkoa kekojärjestysominaisuutta. Tämän selvittämiseksi tutkitaan T:n juurta r. Jos juuri r on lehti, kekojärjestys-ominaisuuden voimassaolo on triviaali asia. Muussa tapauksessa erotetaan kaksi tapausta: Jos juuren r vasen lapsi on sen ainoa lapsisolmu, olkoon s juuren r vasen lapsi. Muuten (molemmat lapset olemassa) olkoon s juuren r se lapsi, jonka avain on pienempi. Jos on k(r) > k(s), pitää kekojärjestysominaisuus palauttaa, mikä tehdään lokaalisti vaihtamalla solmuihin r ja s talletetut avainalkio-parit (kuva 5.7.(c)-(d)). Tämä siirtää kekojärjestysrikkomuksen seuraavalle tasolle ja sitä seuraavallekin, joten tarvitaan vielä kaksi vaihtoa (kuva 5.7.(e)-(h)). Kysymys on keon alaskuplimisesta (down-heap bubbling). Vaihto joko korjaa kekojärjestyksen kuntoon tai siirtää ongelman tason kerrallaan alaspäin. Pahimmassa tapauksessa poisto vie aikaa O(log n). 5. luku 290

(13,W) (5,A) (5,A) (6,Z) (13,W) (6,Z) (15,K) (9,F) (7,Q) (20,B) (15,K) (9,F) (7,Q) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) (16,X) (25,J) (14,E) (12,H) (11,S) (c) (d) Kuva 5.7. (jatkoa) (c) Vaihto kekojärjestysominaisuuden palauttamiseksi lokaalisti (valittiin pienempi lapsista tilanteessa, kun juuren alkio ko. hetkellä oli suurempi kuin molemmat lapset) ja (d) vaihdon jälkeen. 5. luku 291

(5,A) (5,A) (9,F) (6,Z) (9,F) (6,Z) (15,K) (13,W) (7,Q) (20,B) (15,K) (13,W) (7,Q) (20,B) (16,X) (25,J) (14,E) (12,H) (11,S) (16,X) (25,J) (14,E) (12,H) (11,S) (e) (f) Kuva 5.7. (jatkoa) (e) Toinen vaihto (pienempi lapsista) kekojärjestysominaisuuden palauttamiseksi lokaalisti ja (f) vaihdon jälkeen. 5. luku 292

(5,A) (5,A) (9,F) (6,Z) (9,F) (6,Z) (15,K) (12,H) (7,Q) (20,B) (15,K) (12,H) (7,Q) (20,B) (13,W) (16,X) (25,J) (14,E) (11,S) (16,X) (25,J) (14,E) (13,W) (11,S) (g) (h) Kuva 5.7. (loppu) (g) Kolmas eli viimeinen vaihto (pienempi lapsista) kekojärjestysominaisuuden palauttamiseksi lokaalisti ja (h) vaihdon jälkeen. 5. luku 293

Analyysi Taulukossa 5.2. on prioriteettijonon abstraktin tietotyypin suoritusajat kekototeutuksen tapauksessa, kun keko on tehty binääripuuna perustuen tämän abstraktin tietotyypin metodeihin (paitsi elements()), jotka toimivat ajassa O(1). Luvun 4.4. linkitetty rakenne ja sekvenssipohjainen rakenne toteuttavat nämä vaatimukset. Taulukko 5.2. Kekototeutukseen perustuvan prioriteeettijonon suorituskyky, kun keko on tehty linkitetyn rakenteen tai sekvenssipohjaisena binääripuuna. Suoritettaessa metodia on alkioiden määrä prioriteettijonossa n. Linkitetyn binääripuun toteutuksessa tilantarve on O(n) ja sekvenssin tapauksessa O(N), missä N n on sekvenssin taulukon koko. 5. luku 294

Jokainen prioriteettijonon metodi on näin ollen suoritettavissa tehokkaasti eli ajassa O(log n) tai O(1) seuraavista syistä: Keon T korkeus on O(log n), kun T on täydellinen. Pahimmassa tapauksessa ylös- ja alaskuplimiset vaativat aikaa suhteessa keon T korkeuteen. Pahimmassa tapauksessa lisäyspaikan löytäminen metodin insertitem suorituksessa ja viimeisen solmun päivittäminen metodissa removeminelement vaativat aikaa suhteessa kaksi kertaa keon T korkeus. Jälkimmäisessä solmun avainta pitää näet verrata kumpaankin lapseen, jos nämä ovat olemassa. Keossa T on n solmua, joista jokainen viittaa avaimeen ja alkioon. Kaiken kaikkiaan keko on hyvin tehokas prioriteettijonon yhteydessä, kummallakin esitetyllä toteutusvaihtoehdolla. Se on nopeampi kuin sekvenssiin perustuva prioriteettijono. Myös tärkeää on, että se nopeuttaa olennaisesti prioriteettijonolajittelua verrattuna aiempaan sekvenssilähestymistapaan siinä. 5. luku 295

Kekolajittelu Kuten äsken havaittiin, prioriteettijonon toteuttaminen kekona suo hyvin tehokkaat menetelmät, so. logaritmiset tai vakiolliset suoritusajat. Palataan hetkeksi lukuun 5.1., jossa esitettiin prioriteettilajittelun idea. Kun prioriteettijono P toteutetaan kekona, niin sekä ensimmäinen vaihe(n insertitem-operaatiota) ja toinen vaihe (n removemin- Element-operaatiota) vaativat ajan O(log k), missä k on keon alkioiden lukumäärä ao. hetkellä. Kun aina on k n, jokainen sellainen operaatio toimii ajassa O(log n) pahimmassa tapauksessa. Siten kumpikin vaihe vaatii O(n log n) ja samoin koko algoritmi. Kysymys on kekolajittelusta (heap-sort), jonka suorituskyky on todettu seuraavassa lauseessa. Lause 5.2. Kekolajittelualgoritmi lajittelee n vertailtavissa olevan alkion sekvenssin ajassa O(n log n). 5. luku 296

Saatu tulos on paljon parempi kuin neliöllinen aika. Erikoistapauksia on olemassa, missä kekolajittelua voidaan tehostaa tästä yleisestä tuloksesta. Tällainen on tilanne, jossa kaikki avaimet ovat etukäteen tunnetut, jolloin päästään lineaariseen suoritusaikaan. 5. luku 297