7. Keko Tarkastellaan velä yhtä tapaa toteuttaa svulla 6 määrtelty tetotyypp joukko Tällä kertaa emme kutenkaan toteuta normaala operaatovalkomaa, vaan olemme knnostuneta anoastaan kolmesta operaatosta: - heap-nsert(a, k) lsää joukkoon avamen k - heap-mn(a) palauttaa joukon penmmän avamen arvon - heap-del-mn(a) postaa ja palauttaa joukosta penmmän avamen Nämä operaatot tarjoavaa kekoa sanotaan mnmkeoks (engl. mnmum heap) Tonen vahtoehto, el maksmkeko (engl. maxmum heap) tarjoaa operaatot: - heap-nsert(a, k) lsää joukkoon avamen k - heap-max(a) palauttaa joukon suurmman avamen arvon - heap-del-max(a) postaa ja palauttaa joukosta suurmman avamen Nätä kolmea operaatota sanotaan (maksm/mnm) keko-operaatoks Kesktymme tässä luvussa enssjasest maksmkekoon ja sen toteutukseen 9
Huom: Keoks kutsutaan myös mustaluetta, josta suortettaven ohjelmen ajonakanen mustnvaraamnen tapahtuu. Tällä tetojenkästtelyn "tosella" keolla e ole mtään tekemstä tetorakenteen keko kanssa Pystysmme luonnollsest toteuttamaan operaatot käyttäen jo tuntemamme tetorakenteta: käyttämällä järjestämättömän lstan nsert, delete ja max-operaatota heap-nsert ols vakoakanen mutta heap-del-max ves akaa O(n) käyttämällä järjestetyn rengaslstan nsert, delete ja max-operaatota heap-nsert ves akaa O(n) ja heap-del-max ols O() käyttämällä AVL-puun nsert, delete ja max-operaatota sekä heap-nsert että heap-del-max vesvät akaa O(log n) Seuraavassa esttämämme keko-tetotyypn toteutuksessa sekä heap-del-max (ta mnmkeossa heap-del-mn) että heap-nsert tomvat ajassa O(log n) ja heap-max (ta mnmkeossa heap-mn) tom vakoajassa O() 99
Herää kysymys, mhn tarvtsemme nän spesalsotunutta tetorakennetta? Ekö rtä, että käyttäsmme muokattua tasapanotettua hakupuuta, sllä nän saavutettu keko-operaatoden vaatvuus ols O-analyysn melessä sama kun kohta estettävällä varsnasella kekototeutuksella? Tulemme kurssn akana näkemään algortmeja, jotka käyttävät aputetorakenteenaan kekoa ja näden algortmen tehokkaan toteutuksen kannalta keko-operaatoden tehokkuus on oleellnen Vakka keko e tuokaan kertaluokkaparannusta operaatohn, on se toteutukseltaan hyvn kevyt, ja käytännössä esm. AVL-puuhun perustuvaa toteutusta huomattavast nopeamp Keko on ohjelmojalle ktollnen tetorakenne snä melessä, että toteutus on hyvn yksnkertanen tosn kun esm. tasapanosten hakupuden toteutukset Keosta on erlasa versota, kuten bnomkeko ja Fbonacc-keko (jota emme kästtele täällä) 00
Mnmkeon avulla saamme toteutettua tehokkaast prorteettjonon: heap-nsert ve jonottajan jonoon, avan vastaa jonottajan prorteetta (mtä penemp numeroarvo stä korkeamp prorteett) heap-del-mn ottaa jonosta seuraavaks palveltavaks korkemman prorteetn omaavan jonottajan Keon avulla saamme myös toteutettua tehokkaan kekojärjestämsalgortmn Myös verkkoalgortmen yhteydessä (luvussa ) löydämme käyttöä keolle 0
Maksmkeon toteuttamnen Keko kannattaa ajatella bnäärpuuna, joka on talletettu mustn taulukkona Bnäärpuu on maksmkeko jos (K) kakk lehdet ovat kahdella verekkäsellä tasolla k ja k + sten että tason k + lehdet ovat nn vasemmalla kun mahdollsta ja kaklla k:ta ylempen tasojen solmulla on kaks lasta (K) jokaseen solmuun talletettu arvo on suuremp ta yhtäsuur kun solmun lapsn talletetut arvot Seuraava bnäärpuu on maksmkeko 6 0 5 7 6 9 7 9 0 0
Keko-omnasuuden (k) ansosta puu vodaan esttää taulukkona, mssä solmut on lueteltu tasottan vasemmalta okealle Yllä oleva puu taulukkoestyksenä: 5 6 7 9 0 6 0 7 9 juur taso taso taso Käytännössä keko kannattaa ana tallentaa taulukkoa käyttäen Kekotaulukkoon A lttyy kaks attrbuutta A.length kertoo taulukon koon A.heap-sze kertoo montako taulukon pakkaa (alusta alkaen) kuuluu kekoon Huom: Taulukossa A vo ss kohdan heap-sze jälkeen olla "kekoon kuulumattoma" lukuja Keon juuralko on talletettu taulukon ensmmäseen pakkaan A[] 0
Taulukon kohtaan talletetun solmun vanhemman sekä lapset tallettavat taulukon ndekst saadaan selvlle seuraavlla apuoperaatolla: parent() return / left() return rght() return + Vakka varsnasa vttetä e ole, keossa lkkumnen on todella helppoa tarkastellaan edellsen svun esmerkktapausta juuren A[] vasen laps on pakassa A[ ] = A[] ja okea laps pakassa A[ + ] = A[] solmun A[5] vanhemp on A[ 5/ ] =, vasen laps A[0] mutta koska heap-sze[a] = 0, nn okeaa lasta e ole Vomme lausua kekoehdon (K) nyt seuraavast: kaklle < heap-sze pätee A[parent()] A[] 0
Ennen varsnasa keko-operaatota toteutetaan tärkeä apuoperaato heapfy Operaato korjaa kekoehdon, jos se on rkk solmun kohdalla Oletus on, että solmun vasen ja okea alpuu toteuttavat kekoehdon Kutsun heapfy(a, ) jälkeen koko solmusta lähtevä alpuu toteuttaa kekoehdon Tomntaperaate on seuraava: Jos A[] on penemp kun tonen lapsstaan, vahdetaan se suuremman lapsen kanssa Jatketaan rekursvsest alas muuttuneesta lapsesta 05
heapfy:n parametrena ovat taulukko A ja ndeks oletuksena ss on että left() ja rght() vttaavat jo kekoja oleven alpuden A[left()] ja A[rght()] juurn operaato kuljettaa alkota A[] alaspän, kunnes alpuusta jonka juurena A[] on tulee keko heapfy(a,) l = left() r = rght() f r A.heap-sze f A[l] > A[r] largest = l 5 else largest = r 6 f A[] < A[largest] 7 vahda A[] ja A[largest] heapfy(a,largest) 9 elsf l == A.heap-sze and A[]<A[l] 0 vahda A[] ja A[l] Jos molemmat lapset ovat olemassa (rv ), vahdetaan tarvttaessa A[]:n arvo lapssta suuremman arvoon ja kutsutaan lapselle heapfy-operaatota Jos van vasen laps on olemassa ja tämän arvo suuremp kun A[]:n, vahdetaan arvot keskenään (rvt 9 ja 0) 06
Seuraava kuvasarja valottaa operaaton tomntaa 6 6 0 heapfy(a,) 0 5 7 6 9 7 5 7 6 9 7 9 0 9 0 6 heapfy(a,) 0 5 7 6 9 7 9 0 07
heapfy-operaaton suortusaka rppuu anoastaan puun korkeudesta, rekursvsa kutsuja tehdään pahmmassa tapauksessa puun korkeuden verran n-alkosen keon korkeus selväst O(log n) sllä keko on lähes täydellnen bnäärpuu n alkota ssältävälle keolle tehdyn heapfy-operaaton pahmman tapauksen akavaatvuus on ss O(log n) Rekurson taka operaaton tlavaatvuus on O(log n) Operaato on helppo krjottaa myös lman rekursota, jollon se tom vakotlassa Kekoehdosta (K) seuraa suoraan että keon maksmalko on talletettu pakkaan A[] Operaato heap-max ss on trvaal ja ve vakoajan heap-max(a) return A[] 0
Alkon postamnen keosta heap-del-max(a) max = A[] A[] = A[A.heap-sze] A.heap-sze = A.heap-sze - heapfy(a,) 5 return max Tomntadea operaato palauttaa kohdassa A[] olleen avamen keon vmesessä pakassa oleva alko A[A. heap-sze] vedään postetun alkon tlalle ja keon kokoa penennetään yhdellä (rvt ja ) keko on muuten kunnossa mutta kohtaan A[] srretty avan saattaa rkkoa keko-omnasuuden, kutsutaan heapfy operaatota korjaamaan tlanne Operaaton akavaatvuus sama kun heapfy:llä, el O(log n) 09
Esmerkk heap-del-max-operaaton tomnnasta 6 0 5 7 6 5 7 9 0 6 heap del max(a) 6 0 heapfy(a,) 0 6 5 7 6 5 7 5 7 6 5 7 9 9 0
Alkon lsäämnen kekoon heap-nsert(a,k) A.heap-sze = A.heap-sze+ = A.heap-sze whle > and A[parent()] < k A[] = A[parent()] 5 = parent() 6 A[] = k Tomntadea kasvatetaan keon kokoa yhdellä solmulla el tehdään pakka uudelle avamelle kuljetaan nyt keon uudesta solmusta ylöspän ja srretään arvoja samaan akaan yhtä alemmas nn kauan kunnes uudelle alkolle löydetään pakka joka e rko keko-omnasuutta (K) Pahmmassa tapauksessa lsättävä avan vedään puun juureen ja nän käydessä puun korkeudellsen verran avama on valutettu alaspän Operaaton akavaatvuus ss on O(log n) Seuraavalla svulla esmerkk operaaton tomnnasta
heap nsert(5): 6 6 0 0 7 9 9 7 6 6 0 5 0 9 9 7 7
Jotkut sovellukset tarvtsevat keko-operaatota, joka kasvattaa annetussa ndeksssä olevan avamen arvoa heap-nc-key(a,,newk) f newk > A[] A[] = newk whle > and A[parent()] < A[] vahda A[] ja A[parent()] 5 = parent() Tomntadea jos yrtetään penentää avamen arvoa, operaato e tee mtään kopodaan keon kohtaan uus avamen arvo (rv ) jos kasvatettu avan rkkoo keko-omnasuuden (K), vahdetaan sen arvo vanhemman kanssa nn monta kertaa kunnes okea pakka löytyy (rvt -5) Pahmmassa tapauksessa lehdessä olevaa avanta muutetaan ja muutettu avan joudutaan kuljettamaan ana puun juureen saakka Operaaton akavaatvuus on O(log n) Seuraavalla svulla esmerkk operaaton tomnnasta
heap nc key(a,,5) 6 6 0 0 7 9 7 9 5 6 6 0 5 0 5 7 9 7 9
Vastaavast vodaan penentää annetussa ndeksssä olevan avamen arvoa heap-dec-key(a,,newk) f newk < A[] A[] = newk heapfy(a,) Nyt vodaan ss vahtaa avanta ndeksssä joko heap-nc-key(a,, newk):lla ta heap-dec-key(a,, newk), sen mukaan onko newk penemp ta suuremp kun A[] 5
Kekojärjestämnen Oletetaan että A on n-pakkanen kokonaslukutaulukko Seuraava algortm järjestää A:n alkot suuruusjärjestykseen käyttäen kekoa H aputetorakenteena sort-wth-heap(a,n) for = to n heap-nsert(h,a[]) for = n downto A[] = heap-del-max(h) Algortmn akavaatvuus on O(n log n), sllä heap-nsert ja heap-del-max vevät O(log n) ja molempa kutsutaan n kertaa 6
Mutta vomme toma fksummn: operaaton heapfy avulla on helppo rakentaa mstä tahansa taulukosta A keko: lehdet, el pakossa A[ n/ + ],..., A[n] olevat yhden alkon alpuut ovat jo kekoja kutsutaan heapfy lapsellselle kekosolmulle alkaen A[ n/ ]:sta ana juureen A[] ast nän taulukko A muuttuu keoks for-slmukan nvarantt on ss: kaklla < j A. length solmusta j lähtevä alpuu toteuttaa kekoehdon buld-heap(a) A.heap-sze = A.length for = A.length/ downto heapfy(a,) 7
alussa 5 6 7 9 0 6 9 0 7 5 6 6 9 7 0 9 0 7 heapfy(5) 5 6 6 9 7 0 9 0 7 heapfy() 5 6 6 9 7 0 9 0 7 heapfy()
0 5 6 6 9 7 9 0 7 heapfy(); heapfy(5) 6 0 5 7 6 9 7 9 0 heapfy(); heapfy(); heapfy() 6 0 5 7 6 9 7 9 0 5 6 7 9 0 tuloksena 6 0 7 9 9
Kekojärjestämnen tapahtuu seuraavast heap-sort(a) buld-heap(a) for = A.length downto vahda A[] ja A[] A.heap-sze = A.heap-sze 5 heapfy(a,) Tomntadea anestosta tehdään ensn maksmkeko, nän suurn alko on kohdassa A[] vahdetaan keskenään keon ensmmänen ja vmenen alko nän saamme yhden alkon vetyä taulukon loppuun "okealle" pakalleen penentämällä keon kokoa yhdellä huolehdtaan velä että vmenen alko e enää kuulu kekoon pakkaan A[] vety alko saattaa rkkoa keko-omnasuuden huolehdtaan velä että keko-omnasuus sälyy kutsumalla heapfy(a, ) tostetaan samaa nn kauan kun keossa on alkota 0
Kekojärjestämsen akavaatvuus heapfy:n akavaatmus on O(log n) keolle jossa n alkota buld-heap-operaatossa kutsutaan n/ kertaa heapfy keolle, jossa on korkentaan n alkota, ss buld-heap käyttää akaa korkentaan O(n log n) heap-sortssa kutsutaan velä n kertaa heapfy-operaatota kokonasuudessaan kekojärjestämsen akavaatvuus on ss O(n log n) Tlavaatvuus heapfy kutsuu rekursvsest tseään pahmmllaan keon korkeudellsen verran, operaato on kutenkn helppo toteuttaa myös lman rekursota jollon sen tlantarve on vako muutkaan kekojärjestämsen tomet evät aputlaa tarvtse, ss tlavaatvuus O()
Tarkemp analyys paljastaa että operaaton buld-heap akavaatvuus onkn okeastaan van O(n) Operaaton heapfy(a, ) akavaatvuus on O(h()), mssä h() on solmun korkeus puussa Keon korkeus on selväst k = log n Keon tasolla j on korkentaan j solmua ja näden korkeus on k j ta k j, el korkentaan k j Operaaton buld-heap akavaatvuus on ss O( h()), mutta edellsen perusteella h() k j (k j) = j=0 k k k j j = k j( )j < k j( )j n, j=0 koska k n ja kaavakrjasta näemme, että j=0 j( )j = j=0 Seuraavlla svulla on esmerkk kekojärjestämsestä j=0
6 0 0 7 9 7 9 6 0 9 9 7 7 6 0 6 7 7 9 9 0 6 0 6
7 9 7 9 0 6 0 6 7 9 7 9 0 6 0 6 tuloksena 7 9 0 6
Mstä johtuu, että pkajärjestämnen tom käytännössä usemmten nopeammn kun kekojärjestämnen? Kekojärjestämnen vahtaa hyvn usen täysn er puollla järjestettävää taulukkoa oleven alkoden arvoja, pkajärjestämnen taas pysyttelee usemmten ptemmän akaa penemmässä osassa taulukkoa Tällä on suur merktys käytännössä, sllä jos mustvttaukset keskttyvät tetyllä ajanjaksolla peneen osaan taulukkoa, on todennäkösempää että taulukon tarvttava osa löytyy välmuststa Välmustn tehtäven musthakujen vemä aka on merkttäväst penemp verrattuna shen, että teto jouduttasn hakemaan keskusmuststa Asan merktys korostuu velä enemmän, jos koko järjestettävä taulukko e mahdu kerralla keskusmustn, vaan sjatsee osttan kntolevyn swap-osossa Käytännössä on myös osottautunut, että kekojärjestämnen suorttaa keskmäärn monta kertaa enemmän vertalu- ja sjotusoperaatota kun pkajärjestämnen E ole mtenkään lmestä mstä tämä sekka johtuu. 5
Keko käytännössä Monssa käytännön sovelluksssa, esm. käytettäessä kekoa prorteettjonona, keottavat alkot ssältävät mutakn attrbuutteja kun pelkän avamen Tällön tse kekoon e välttämättä kannata tallettaa muuta kun avamet sekä vtteet avameen lttyvään muun datan tallettavaan oloon Tällasessa käytännön tlanteessa keko-operaatoden parametrt kannattanee valta seuraavast - heap-nsert(a, x, k) lsää kekoon olon x, jolla avan k - heap-max(a) palauttaa vtteen oloon jolla on avamena keon maksmarvo - heap-del-max(a) palauttaa vtteen oloon jolla on avamena keon maksmarvo ja postaa olon lttyvän avamen keosta - heap-nc-key(x, newk) kasvattaa olon x avanta antaen slle uuden arvon newk - heap-dec-key(x, newk) penentää olon x avanta antaen slle uuden arvon newk Jotta operaato heap-nc-key saadaan toteutettua tehokkaast datan tallettavssa olossa on myös oltava vte vastaavaan kekoalkoon Käytännössä vtteet ss ovat kekotaulukon ndeksejä el kokonaslukuja 6
Mustn organsont näyttää esm. seuraavalta: 9 7 keko vtteet datan tallettavn olohn datan tallettavat olot......... vtteet kekosolmuhn 7