5. Puut. Kuva 5.1. Sukupuu Puun abstrakti tietotyyppi

Samankaltaiset tiedostot
4. Puut. Kuva 4.1. Sukupuu Puun abstrakti tietotyyppi

puuta tree hierarkkinen hierarchical

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

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

A TIETORAKENTEET JA ALGORITMIT

Hakupuut. tässä luvussa tarkastelemme puita tiedon tallennusrakenteina

Algoritmit 2. Luento 2 Ke Timo Männikkö

Algoritmit 2. Luento 2 To Timo Männikkö

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

Algoritmit 2. Luento 4 To Timo Männikkö

Algoritmit 1. Luento 7 Ti Timo Männikkö

1.1 Tavallinen binäärihakupuu

Algoritmit 1. Luento 8 Ke Timo Männikkö

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.

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

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

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

Algoritmit 2. Luento 4 Ke Timo Männikkö

Kysymyksiä koko kurssista?

Tietorakenteet, laskuharjoitus 3, ratkaisuja

7. Tasapainoitetut hakupuut

Binäärihaun vertailujärjestys

Algoritmit 2. Luento 7 Ti Timo Männikkö

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

A TIETORAKENTEET JA ALGORITMIT KORVAAVAT HARJOITUSTEHTÄVÄT 3, DEADLINE KLO 12:00

Tietorakenteet ja algoritmit - syksy

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.

811312A Tietorakenteet ja algoritmit, , Harjoitus 7, ratkaisu

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

Algoritmit 2. Luento 6 To Timo Männikkö

3. Binääripuu, Java-toteutus

Tietorakenteet, laskuharjoitus 6,

Algoritmit 2. Luento 5 Ti Timo Männikkö

Tietorakenteet ja algoritmit

1 Puu, Keko ja Prioriteettijono

Luku 8. Aluekyselyt. 8.1 Summataulukko

Algoritmit 2. Luento 6 Ke Timo Männikkö

Algoritmit 1. Luento 12 Ti Timo Männikkö

Algoritmit 1. Luento 1 Ti Timo Männikkö

Algoritmit 1. Luento 3 Ti Timo Männikkö

A TIETORAKENTEET JA ALGORITMIT

Tietorakenteet, laskuharjoitus 7, ratkaisuja

Algoritmit 1. Luento 12 Ke Timo Männikkö

1.1 Pino (stack) Koodiluonnos. Graafinen esitys ...

Algoritmit 1. Luento 6 Ke Timo Männikkö

Algoritmit 2. Luento 5 Ti Timo Männikkö

Muita linkattuja rakenteita

10. Painotetut graafit

Rajapinta (interface)

Kierros 4: Binäärihakupuut

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

TIE448 Kääntäjätekniikka, syksy Antti-Juhani Kaijanaho. 9. marraskuuta 2009

Algebralliset tietotyypit ym. TIEA341 Funktio ohjelmointi 1 Syksy 2005

CS-A1140 Tietorakenteet ja algoritmit

18. Abstraktit tietotyypit 18.1

811312A Tietorakenteet ja algoritmit, , Harjoitus 5, Ratkaisu

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

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

811312A Tietorakenteet ja algoritmit III Lajittelualgoritmeista

4. Sekvenssit Astetta soveltavat sekvenssit

TKT20001 Tietorakenteet ja algoritmit Erilliskoe , malliratkaisut (Jyrki Kivinen)

8. Lajittelu, joukot ja valinta

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

ITKP102 Ohjelmointi 1 (6 op)

58131 Tietorakenteet (kevät 2008) 1. kurssikoe, ratkaisuja

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

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

13. Loogiset operaatiot 13.1

Koe ma 1.3 klo salissa A111, koeaika kuten tavallista 2h 30min

Algoritmi on periaatteellisella tasolla seuraava:

58131 Tietorakenteet ja algoritmit (kevät 2013) Kurssikoe 1, , vastauksia

Algoritmit 1. Luento 10 Ke Timo Männikkö

Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

6. Sanakirjat. 6. luku 298

Harjoitus Olkoon olemassa luokat Lintu ja Pelikaani seuraavasti:

Algoritmit 1. Luento 4 Ke Timo Männikkö

Algoritmit 2. Luento 9 Ti Timo Männikkö

58131 Tietorakenteet ja algoritmit (kevät 2014) Uusinta- ja erilliskoe, , vastauksia

Datatähti 2019 loppu

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Taulukot & Periytyminen

Ohjelmoinnin jatkokurssi, kurssikoe

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

Algoritmit 2. Luento 14 Ke Timo Männikkö

14 Tasapainotetut puurakenteet

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

815338A Ohjelmointikielten periaatteet Harjoitus 2 vastaukset

Java kahdessa tunnissa. Jyry Suvilehto

58131 Tietorakenteet ja algoritmit (syksy 2015)

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

9. Periytyminen Javassa 9.1

B + -puut. Kerttu Pollari-Malmi

Tietorakenteet (syksy 2013)

Stabiloivat synkronoijat ja nimeäminen

Luku 7. Verkkoalgoritmit. 7.1 Määritelmiä

Johdatus graafiteoriaan

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

Rajapinnasta ei voida muodostaa olioita. Voidaan käyttää tunnuksen tyyppinä. Rajapinta on kuitenkin abstraktia luokkaa selvästi abstraktimpi tyyppi.

15. Ohjelmoinnin tekniikkaa 15.1

Transkriptio:

5. Puut Aabraham Seuraavaksi käsitellään yhtä tärkeimmistä tietojenkäsittelytieteen ei-lineaarisista käsitteistä, puuta (tree). Puut ovat olleet keksintönä todellinen läpimurto, koska niissä luotiin tehokas eilineaari tapa järjestää nopeasti tietoa lineaarisiin tietorakenteisiin verrattuna. Jaakob Iisak Eesau Ismael Järjestyssuhteet ovat puissa monipuolisemmat kuin pelkät edeltäjä ja seuraaja -relaatiot lineaaristen tietorakenteiden yhteydessä. Puussa suhde on hierarkkinen (hierarchical), jolloin jotkut oliot ovat toisten yläpuolella ja toiset alapuolellla. Tällöin käytetään yleensä nimikkeitä vanhempi, lapsi, esi-isä ja jälkeläinen. Nimikkeiden metafoora voisi tulla kenties sukupuista, kuten kuvassa 5.1. Ruuben Simeon Leevi Kuva 5.1. Sukupuu. 5. kappale 157 5. kappale 158 Kännykkä Oyj 5.1. Puun abstrakti tietotyyppi T & K myynti osto tuotanto Puu on abstrakti tietotyyppi, joka tallettaa alkiot hierarkkisesti. Päällimmäistä alkiota lukuunottamatta jokaisella alkiolla, solmulla (node), on vanhempi tai isä (parent, father) ja mahdollisesti lapsia (children). Toisinaan käytetään myös termejä edeltäjä ja seuraaja. Päällimmäistä solmua sanotaan juureksi (root), ja puu esitetään ylösalaisin (helpompi piirtää kasvusuunta alaspäin) kuvan 5.2. tavoin. kotimaa ulkomaat pohjoismaat muu Eurooppa muut maanosat laitteisto Afrikka Amerikat Aasia Australia ohjelmisto Kuva 5.2. Kuvitteellisen yrityksen organisaatiokaavio puuna. Juurena on itse yritys. Sen lisäksi sisäsolmuina ovat myynti, tuotanto, ulkomaat ja muut maanosat. Ulkosolmuina eli lehtinä ovat muut solmut. 5. kappale 159 5. kappale 160

Nimikkeitä ja perusominaisuuksia Puulla T on juurisolmu r, jolla ei ole vanhempaa. Jokaisella puun T muulla solmulla v on vanhempisolmu u. Juuri r on puun T jokaisen solmun v esivanhempi tai -isä (ancestor). Esivanhemman täsmällinen määritelmä on esitettävissä rekursion avulla. Solmun esivanhempi on nimittäin joko solmu itse tai solmun vanhemman esivanhempi. Voidaan ilmaista kääntäen, että solmu v on solmun u jälkeläinen (descendant), jos u on solmun v esivanhempi. Esim. 5.1. Java-ohjelman luokkienvälinen periytyminen muodostaa puun. Luokka java.lang on kaikkien muiden esivanhempi. Jos solmu u on solmun v vanhempi, niin v on solmun u lapsi (child). Vanhemman lapset ovat sisaruksia (siblings). Solmu on ulkosolmu (external node) eli lehti (leaf), jos sillä ei ole lapsia, muuten se on sisäsolmu (internal node). Puun alipuu (subtree) solmussa v käsittää v:n lisäksi kaikki tämän jälkeläiset puussa. Esim. 5.2. Tiedostot järjestetään useimmissa käyttöjärjestelmissä hierarkkisesti sisäkkäisiksi hakemistoiksi, jotka esitetään puuna (kuva 5.3.). Hakemistot muodostavat puun sisäsolmut ja tiedostot lehdet. Unix-käyttöjärjestelmässä puun juurta kutsutaan juurihakemistoksi (merk. symbolilla /). Se on tiedostojärjestelmän kaikkien hakemistojen esivanhempi. 5. kappale 161 5. kappale 162 tietorakenteet /user/rt/kurssit neurolaskenta Puu on järjestetty (ordered), jos lasten välillä on määritelty lineaarinen järjestys, jolloin solmun lapset voidaan tunnistaa ensimmäisenä, toisena jne. Järjestystapa riippuu käyttötarkoituksesta. Tavallisesti se esitetään piirtämällä sisarussolmut vasemmalta oikealle. l1 luennot... l26 Puu T on solmujen joukko, joka tallettaa alkiot vanhempi-lapsisuhteessa: viikkoharjoitukset vh1... vh13 harjoitustyö Kuva 5.3. Tämä on tiedostojärjestelmän osa, jossa tietorakenteet-kurssin alipuu sisältää 43 solmua. Esim. 5.3. Rakenteinen dokumentti, kuten kirja, voidaan järjestää hierarkkisesti puuna, jonka sisäsolmut ovat lukuja ja alilukuja ja jonka ulkosolmut eli lehdet ovat tekstikappaleita, taulukoita, kuvia, bibliografia jne. Juuri vastaa itse kirjaa. Tekstikappaleet voitaisiin vielä jakaa virkkeisiin, lauseisiin ja sanoihin, jotka sisältävät merkkejä. Puu on tässä esimerkki järjestetystä, sillä solmujen lapsien välillä on hyvin määritelty järjestys (kuva 5.4.). 5. kappale 163 5. kappale 164

kirja esipuhe osa A osa B viitteet kpl kpl 1. luku 5. luku 6. luku 9. luku kpl kpl 1.1 luku 1.4 luku 5.1 luku 5.7 luku 6.1 luku 6.5 luku 9.1 luku 9.6 luku Binääripuu (binary tree) on järjestetty puu, jossa solmuilla on joko ei yhtään tai kaksi lasta. Tämä määritellään toisinaan aidoksi (strict) binääripuuksi, jolloin voidaan sanoa binääripuun solmun käsittävän ei yhtään, yhden tai kaksi lasta. Tässä esityksessä pitäydytään ensimmäiseen määritelmään ja jälkimmäinen, ei-aito tapaus, mielletään yleisen puun erääksi tapaukseksi. Tällöin binääripuun solmun ensimmäinen lapsi on vasen (left) ja toinen oikea (right). Solmun vasemman ja oikean lapsen mukaan nimetään ne juurinaan vasen ja oikea alipuu (subtree). kpl kpl kpl kpl Binääripuilla on lukuisia sovelluksia. Kaksi kuvataan suppeasti seuraavissa esimerkeissä. Kuva 5.4. Kirjan rakenne esitetty puuna. 5. kappale 165 5. kappale 166 kuulovaurio? kyllä ei Esim. 5.4. Binääripuut ovat tärkeitä yhteyksissä, joissa halutaan esittää erilaisia tuloksia riippuen joidenkin testien tuloksista, joihin vastataan kyllä tai ei tai ylipäänsä jollakin binäärivastauksella (kaksiarvoisella muuttujalla). Kysymys on päätöspuista (decision trees). Näissä lehtisolmu v edustaa päätöstä, johon päädytään tilanteessa, että vastaukset esitettyihin kysymyksiin ovat puun kyseisen haaran mukaiset, ts. johtavat solmuun v. Kuvassa 5.5. on eräs esimerkki yksinkertaisesta päätöspuusta huimauspotilaiden korvalääketieteellisessä tasapainotutkimuksessa. ei hyvänlaatuista asentohuimausta huimauskohtausten esiintymismäärä? 1 > 1 ei hyvänlaatuista asentohuimausta ei hyvänlaatuinen asentohuimaus päävamma? kyllä ei hyvänlaatuista asentohuimausta Kuva 5.5. Binääri päätöspuu tutkittaessa huimauspotilaalta hyvänlaatuisen asentohuimauksen mahdollisuutta diagnoosina. 5. kappale 167 5. kappale 168

Esim. 5.5. Aritmeettinen lauseke on esitettävissä puuna, jonka lehdet sisältävät muuttujat ja vakiot ja sisäsolmut sisältävät operaattoreita +, -, ja / (kuva 5.6.). Puun jokaisella solmulla on arvonaan jokin lausekkeen symboli. Jos solmu on lehti, sen arvo on muuttuja tai vakio. Jos se on sisäsolmu, sen arvo on lapsiin liittyvä operaatio. Tällainen aritmeettinen lauseke kuvataan binääripuuna, koska kullakin operaattorilla on täsmälleen kaksi operandia. Lisäksi pitää huomata operandeilla olevan määrätty järjestys, joka vaikuttaa tulokseen (paitsi operaattoreilla + ja ). Kääntäjän kääntäessä korkeantason kieltä väli- tai konekieleksi prosessin aikana aritmeettiset lausekkeet kuvataan abstraktilla tasolla puina, joista kuvan 5.6. tapaus voisi olla rajoitettu tapaus. / + + 3 2 3 3 1 9 5 + 7 4 Kuva 5.6. Tämä on aritmeettista lauseketta esittävä binääripuu. Lauseke on (((3 + 1) 3) / (9 5) + 2)) ((3 (7 4)) + 6)). 6 5. kappale 169 5. kappale 170 Puun metodit Puun abstrakti tietotyyppi tallettaa alkiot paikkoihin, jotka sekvenssin tapaan määritellään suhteessa naapuripaikkoihin. Puun paikat (position) ovat solmuja. Naapuripaikat toteuttavat vanhempi-lapsi-suhteen, joka määrittelee kelvollisen puun. Näin ollen paikka puussa tarkoittaa solmua siinä. Puussa ovat seuraavat metodit merkittäviä: element(): Palauttaa alkion kyseisestä solmusta. Tulos: alkio container(): Palauttaa viittauksen puuhun, joka sisältää kyseisen solmun. Tulos: puu 5. kappale 171 Puun todellinen voima syntyy metodeista, jotka käyttävät solmuja: root(): Palauttaa puun T juurisolmun. Puun ollessa tyhjä esiintyy virhe. Tulos: solmu parent(v): Palauttaa solmun v vanhemman. Solmun v ollessa juuri esiintyy virhe. Syöte: solmu Tulos: solmu children(v): Palauttaa luettelon solmun v lapsista. Syöte: solmu Tulos: luettelo solmuista Jos puu T on järjestetty, lapsien luettelo antaa ne järjestyksessä. Jos solmu v on lehti, luettelo on tyhjä. 5. kappale 172

Edellisten lisäksi myös seuraavat ovat monesti tarpeen: isinternal(v): Testaa, onko solmu v sisäsolmu. Syöte: solmu Tulos: totuusarvo isexternal(v): Testaa, onko solmu v ulkosolmu eli lehti. Syöte: solmu Tulos: totuusarvo isroot(v): Testaa, onko solmu v juuri. Syöte: solmu Tulos: totuusarvo Nämä metodit ovat käteviä erilaisissa testeissä, jotka toteutetaan if-lauseissa ja while-silmukoissa. Oheiset yleisluontoiset metodit, jotka eivät sinänsä riipu puusta tietorakenteena, ovat hyödyllisiä puiden yhteydessä: size(): Palauttaa puun T solmujen lukumäärän. Tulos: kokonaisluku isempty(): Testaa, onko puussa T solmuja vai ei. Tulos: totuusarvo elements(): Palauttaa puun T solmuihin talletetut kaikki alkiot luettelona. Tulos: alkioiden luettelo positions(): Palauttaa puun T kaikkien solmujen (paikkojen) luettelon. Tulos: solmujen luettelo 5. kappale 173 5. kappale 174 Puun rajapinta Javalla swap(v,w): Vaihtaa puun T solmujen v ja w alkiot keskenään. Syöte: kaksi solmua replace(v,e): Korvaa solmun v alkion alkiolla e ja palauttaa aiemman alkion. Syöte: solmu ja alkio Tulos: alkio Mitään erityisiä puun päivitysmetodeja ei tässä määritellä, vaan palataan niihin myöhemmin, lähinnä esimerkkien yhteydessä. Tarkastellaan kuitenkin seuraavaksi, miten em. metodit voidaan toteuttaa rajapintana Javalla. Puun geneeriset metodit on mainittu koodin 5.1. yhteydessä, ja koodin 5.2. rajapinta laajentaa edellistä. Kyseessä voivat olla mielivaltaisesti valitut oliot, ja SimpleTree perustuu paikan (solmun) abstraktioon. public interface PositionalContainer { // query methods public int size(); // return the size of the container public boolean isempty(); // return whether the container is // empty or not public Enumeration elements(); // return the elements in the // container Koodi 5.1. PositionalContainer-rajapinta. 5. kappale 175 5. kappale 176

public Enumeration positions(); // return the positions in the // container // update methods public void swap(position v, Position w); // swap elements at v // and w public Object replace(position v, Object e); // replace with e and return the element at v Koodi 5.1. (jatkoa) PositionalContainer-rajapinta. public interface SimpleTree extends PositionalContainer { // accessor methods public Position root(); // return the root of the tree public Position parent(position v); // return the parent of v public Enumeration children(position v); // return the children of v // query methods public boolean isinternal(position v); // test whether v is internal public boolean isexternal(position v); // test whether v is // external public boolean isroot(position v); // test whether v is the root of // the tree Koodi 5.2. SimpleTree-rajapinta 5. kappale 177 5. kappale 178 5.2. Puiden perusalgoritmeja Kuvataan puunkäsittelyalgoritmeja, jotka suorittavat tehtävänsä päästen puuhun käsiksi koodin 5.2. rajapinnalla. Seuravat ajoaikaoletukset tehdään metodeille, jotka toteuttavat rajapinnan, olivatpa ne sitten mistä tahansa luokasta. Geneeriset metodit size(), isempty(), swap(v,w) ja replace(v,e) toimivat ajassa. Metodit root() ja parent(v) toimivat ajassa. Boolen-tyyppiset metodit isinternal(v), isexternal(v) ja isroot(v) toimivat myös ajassa. Metodit elements() ja positions() toimivat ajassa O(n), jossa n on puun solmujen lukumäärä. 5. kappale 179 Metodi children(v) toimii ajassa O(c v ), jossa c v on solmun v lasten lukumäärä. Metodien elements() ja children(v) tuloksille metodit hasmoreelements() ja nextelement() toimivat ajassa. Myöhemmin, luvussa 5.4. esitetään tietorakennetoteutukset, jotka täyttävät nämä suoritusaikaoletukset. Sitä ennen käsitellään vielä muutamia perusasioita ja kuinka näitä metodeja sovelletaan puuongelmien ratkaisemisessa. Syvyys ja korkeus Olkoon v puun T solmu. Solmun v syvyys (depth) on sen esivanhempien lukumäärä poislukien solmu itse. Esim. kuvan 5.2. solmun ulkomaat syvyys on 2. Täten juuren syvyys on 0. Solmun syvyys on määriteltävissä rekursiivisesti: 5. kappale 180

Jos v on juuri, sen syvyys on 0. Muuten solmun v syvyys on yksi lisättynä solmun v vanhemman syvyydellä. Määritelmän mukaisesti koodin 5.3. algoritmi laskee solmun v syvyyden puussa T. Algoritmin suoritusaika on O(d v ), missä d v viittaa solmun v syvyyteen, sillä algoritmi suorittaa vakioaikaisen rekursiivisen askeleen jokaiselle solmun v esivanhemmalle. Huomaa, että tämän osan (5.2.) Java-esimerkit voivat olla hieman epäyhtenäisiä, joten nämä on syytä lukea ikään kuin pseudokoodina. public int depth (SimpleTree T, position v) { if (T.isRoot(v)) return(0); else return(1+depth(t.parent(v))); Koodi 5.3. Metodi depth solmun syvyyden tutkimista varten. 5. kappale 181 5. kappale 182 Solmun v korkeus (height) puussa T määritellään niin ikään rekursiivisesti: Jos v on ulkosolmu eli lehti, sen korkeus on 0. Muuten solmun v korkeus on yksi lisättynä solmun v lapsen maksimikorkeudella. public int height1 (SimpleTree T) { // computation of the height of a tree by taking the maximum of the // depths of its external nodes int h = 0; Enumeration nodes_of_t = T.positions(); Koko puun korkeus on sama kuin juuren korkeus. Esim. kuvan 5.2. puun korkeus on 4. Puun korkeuden voi mieltää myös sen maksimisyvyytenä jostakin solmusta, joka on sen lehti. Seuraavassa on algortmi korkeuden laskemista varten. Koodi 5.4. (alkuosa) Metodi height1 solmun korkeuden laskemista varten. 5. kappale 183 5. kappale 184

while (nodes_of_t.hasmoreelements()) { Position v = (Position) nodes_of_t.nextelement(); if (T.isExternal(v)) h = Math.max(h,depth(T,v)); return h; Koodi 5.4. (loppuosa) Metodi height1 solmun korkeuden laskemista varten. Mukana on tyypinmuunnos geneerisestä oliosta Position-olioon sekä metodin max käyttämistä. Koodin 5.4. metodi height1 on hyvin yksinkertainen toimintaidealtaan. Se käy läpi kaikki solmut, laskee lehtien korkeudet koodin 5.3. metodin depth avulla ja lopuksi antaa suurimman korkeuden. Algoritmi height1 on melko tehoton. Olkoon d v jälleen solmun v syvyys. Koska d v h + 1 n, missä h on puun T korkeus ja n solmujen määrä, voidaan todeta algoritmin depth(v) pahimman tapauksen kompleksisuuden olevan O(h), joka on O(n). Tästä seuraa, että height1 toimii ajassa O(Σ v T d v ), joka on O(nh) eli O(n 2 ) pahimmassa tapauksessa. 5. kappale 185 5. kappale 186 Seuraava lause on monesti hyödyllinen, ja se osoittaa em. puun rekursiivisen korkeuden laskemisen olevan lineaarinen aikakompleksisuudeltaan. Korkeuden laskeminen voidaan tehdä edellistä tehokkaammin ajassa O(n), kun lasketaan rekursiivisesti puun ja sitten sen lasten alipuiden korkeus. Tästä tulee luokkaa O(c v ) olevaa laskentaa, missä c v on solmun v lasten lukumäärä. Kun kukin iteraatio vaatii aikaa, niin jokaiselle solmulle tarvitaan siis aikaa O(c v ), ja kaikkiaan sitä vaaditaan O( c v v T ). Lause 5.1. Olkoon T puu, jossa on n solmua, ja olkoon c v puun T solmun v lasten lukumäärä. Silloin on c v v T = n 1. Perustelu: Lukuunottamatta juurta puun T jokainen solmu on jonkin toisen solmun lapsi, mikä aikaansaa mainitun sarjan summan. 5. kappale 187 5. kappale 188

Puun kulkeminen esijärjestyksessä Puun T kulkeminen (traversal) merkitsee järjestelmällistä tapaa vuorotellen käydä solmuissa eli saada kukin solmu. Kuljettaessa puuta esijärjestyksessä (preorder) käydään ensin juuressa ja sitten tämän alipuissa käymällä lapset rekursiivisesti läpi. Mitä toiminta käynti solmussa merkitsee kulloinkin, riippuu sovelluksesta. Esijärjestys-algoritmin pseudokoodi on kuvattu koodissa 5.5., jossa sitä aluksi kutsutaan käskyllä preorder(t,t.root()). Algorithm preorder(t,v): käy solmussa v for solmun v jokaiselle lapselle w do käy rekursiivisesti alipuu juureltaan w kutsuen sitä käskyllä preorder(t,w) Koodi 5.5. Esijärjestys-algoritmi. Puun kulkeminen esijärjestyksessä on hyödyllinen haluttaessa solmut lineaarisessa järjestyksessä, jossa vanhemmat tulevat ennen järjestyksessä käytäviä lapsiaan. 5. kappale 189 5. kappale 190 otsikko abstrakti 1.1. 1. johdanto julkaisu 2. menetelmät 3. tulokset viitteet 1.2. 2.1. 2.2. 2.3. 3.1. 3.2. Kuva 5.7. Puun käynti esijärjestyksessä, jossa jokaisen solmun lapset ovat järjestettyinä vasemmalta oikealle. Esim. 5.6. Dokumenttiin liittyvä, kuten esim. 5.3., puun esijärjestyksessä kulkeminen tuottaa perättäisesti koko dokumentin. Puun kulkeminen esijärjestyksessä on tehokas toiminto. Olkoon puussa T solmuja n, ja oletetaan käynnin solmussa vievän aikaa. Tällöin analyysi muistuttaa edellä mainittua tehokasta puun korkeuden laskentaa, joka toimi lineaarisessa ajassa. Esijärjestys-algorimin ei-rekursiivinen osa vaatii jokaisessa solmussa v aikaa O(c v ), missä c v on solmun v lasten lukumäärä. Lauseen 5.1. mukaan esijärjestys-algoritmin suoritusajaksi tulee O(n). Koodin 5.6. algoritmi kulkee rekursiivisesti puun esijärjestyksessä ja tulostaa kunkin solmun alkion. 5. kappale 191 5. kappale 192

public void preorderprint (SimpleTree T, Position v) { // Preorder traversal of the subtree rooted at node v that prints to // the standard output the elements stored at the nodes in the // order they are visited. It assumes that the elements support // tostring(). System.out.println(T.element(v)); Enumeration children_of_v = T.children(v); while (children_of_v.hasmoreelements()) { Position w = (Position) children_of_v.nextelement(); preorderprint(t,w); // recursive call Koodi 5.6. Algoritmi preorderprint. Puun kulkeminen esijärjestyksessä on hyödyllistä, kun pitää ratkaista puuongelma, jossa on suoritettava laskentaa solmulle ennen tämän jälkeläisiä. Puun kulkeminen jälkijärjestyksessä Toinen tärkeä puussakulkemisalgoritmi on jälkijärjestys tai loppujärjestys (postorder). Tämä voidaan ymmärtää esijärjestyksen vastakohtana, sillä se käy rekursiivisesti läpi aluksi juuren lasten muodostamat alipuut ja sitten juuren (koodi 5.7). Jos puu on järjestetty, solmun lasten käsittelyn rekursiiviset kutsut tehdään ko. järjestyksen mukaisesti. 5. kappale 193 5. kappale 194 Algorithm postorder(t,v): for solmun v jokaiselle lapselle w do käy rekursiivisesti solmu w juurena alipuu kutsumalla algoritmia postorder(t,w) käy solmussa v otsikko abstrakti 1. johdanto julkaisu 2. menetelmät 3. tulokset viitteet Koodi 5.7. Jälkijärjestys-algoritmi. 1.1. 1.2. 2.1. 2.2. 2.3. 3.1. 3.2. Menetelmän nimi tulee käyntijärjestyksestä, jossa ensin käydään solmun lapsissa ennen itse solmua (kuva 5.8.). Menetelmän analyysi on samankaltainen kuin esijärjestyksellä. Se on O(n), kun yhdessä solmussa käynti vaatii vakioajan. Kuva 5.8. Puun kuvasta 5.7. käynti jälkijärjestyksessä. 5. kappale 195 5. kappale 196

Jälkijärjestyksessä kulkemisesta on esimerkkinä rekursiivinen metodi postorderprint koodina 5.8., jossa solmu tulostetaan siinä käytäessä. public void postorderprint (simpletree T, Position v) { // Postorder traversal of the subtree rooted at node v that prints to // the standard output the elements stored at the nodes in the // order they are visited. Enumeration children_of_v = T.children(v); while (children_of_v.hasmoreelements()) { Position w = (Position) children_of_v.nextelement(); postorderprint(t,w); // recursive call System.out.println(v.element()); // Assumes elements implement // tostring() Koodi 5.8. Algoritmi postorderprint. 5. kappale 197 Jälkijärjestyksessä puun kulkeminen on hyödyllistä ratkaistaessa ongelmia, joissa halutaan laskea solmun jokin ominaisuus, mutta tämä edellyttää, että aiemmin on laskettu solmun lasten vastaava ominaisuus. Luonnollisesti muitakin puiden kulkemisjärjestyksiä on olemassa, kuten myöhemmin binääripuiden yhteydessä esitettävä välijärjestys, mutta esi- ja jälkijärjestykset ovat yksinkertaisia ja usein hyödyllisiä menetelmiä. 5. kappale 198 5.3. Binääripuut Erityisen merkittävä puulaji on binääripuut (binary trees). Binääripuu on järjestetty, jossa jokaisella sisäsolmulla on kaksi lasta. Kuten edellä mainittiin, tässä noudatetaan määritelmää, että binääripuu on aito, ts. siinä ei ole solmuja, joilla olisi vain yksi lapsi. Tämä menettelytapa ei kuitenkaan aiheuta mitään hankaluutta yleisyyden kannalta, sillä epäsopivat puut ovat muunnettavissa määritelmän mukaisiksi binääripuiksi. Binääripuilla on paljon sovelluksia, joista nähtiin edellä esim. 5.4. päätöspuista ja esim. 5.5. aritmeettisen lausekkeen esityksestä. Binääripuiden ominaisuuksia Binääripuilla on useita mielenkiintoisia ominaisuuksia, kuten seuraava. Lause 5.2. Lehtien lukumäärä binääripuussa T on yksi enemmän kuin sisäsolmujen määrä. Perustelu: Tämä osoitetaan jakamalla solmut lehti- ja sisäsolmuihin. Yksinkertaisimmillaan, kun T käsittää vain juuren, siinä on pelkästään yksi lehti, jolloin lause on tosi. Muussa tapauksessa poistetaan puusta T (mielivaltaisesti valittu) lehti v ja sen vanhempi u, joka on sisäsolmu. Näin poistetaan kerrallaan yksi solmu kumpaakin tyyppiä. Jos solmulla u oli vanhempi w, yhdistetään w solmun v entiseen sisarukseen kuvan 5.9. tapaan. 5. kappale 199 5. kappale 200

w u w z v z z w Kyseinen operaatio removeaboveexternal(v) poistaa yhden sisäsolmun ja yhden lehden eli ulkosolmun, mutta puu pysyy edelleen binäärisenä. Toistaen operaatiota päästään lopulta yksittäisen lehden tilanteeseen. Täten on osoitettu, että lehtiä on yksi enemmän kuin sisäsolmuja. (a) Kuva 5.9. Operaatio removeaboveexternal(v) poistaa lehden ja sisäsolmun. (b) (c) Puun T kaikkien samalla syvyydellä d olevien solmujen joukkoa kutsutaan tasoksi (level). Tasolla 0 on juuri, tasolla 1 on enintään kaksi solmua (juuren lapset), tasolla 2 on enintään neljä solmua jne. Yleisesti tasolla d on enintään 2 d solmua (kuva 5.10.). 5. kappale 201 5. kappale 202 taso 0 1 2 3 solmuja 1 2 4 8 Oheiset ominaisuudet ovat johdettavissa kuvan 5.10. havainnollistamana koskien binääripuun korkeutta ja solmujen määrää. Lause 5.3. Olkoon T binääripuu, jossa on n solmua ja h puun korkeus. Silloin puulla T on seuraavat ominaisuudet: 1. Puun T lehtien määrä on vähintään h+1 ja enintään 2 h. 2. Puun T sisäsolmujen määrä on vähintään h ja enintään 2 h -1. 3. Puun T solmujen kokonaismäärä on vähintään 2h+1 ja enintään 2 h+1-1. 4. Puun T korkeus on vähintään log(n+1)-1 ja enintään (n-1)/2 eli log(n+1)-1 h (n-1)/2. Kuva 5.10. Maksimimäärät solmuja binääripuun tasoilla. Jääkööt näiden perustelut lukijan pohdittaviksi. 5. kappale 203 5. kappale 204

Lauseen 5.3. tärkeä seuraus on se, että n-solmuisen binääripuun minimikorkeus on Θ(log n), jolla on huomattavaa merkitystä puita hyödyntävien algoritmien kompleksisuuksille, kuten myöhemmin nähdään. Binääripuun Java-kielinen rajapinta Binääripuun abstrakti tietotyyppi käsittää yleisen puun metodien lisäksi erityisesti seuraavat (paikka tarkoittaa tässä solmua): leftchild(v): Palauttaa solmun v vasemman lapsen. Virhe esiintyy, mikäli v on lehti. Syöte: paikka Tulos: paikka rightchild(v): Palauttaa solmun v oikean lapsen. Virhe esiintyy, mikäli v on lehti. Syöte: paikka Tulos: paikka sibling(v): Palauttaa solmun v sisaruksen. Virhe esiintyy, mikäli v on juuri. Syöte: paikka Tulos: paikka Niin ikään sisällytetään lisäysmetodi: expandexternal(v): Muuttaa lehden v sisäsolmuksi luomalla kaksi uutta lehteä ja asettamalla nämä solmun v lapsiksi. Virhe esiintyy, mikäli v on sisäsolmu. Syöte: paikka 5. kappale 205 5. kappale 206 Myös poistometodi on tarpeen: removeaboveexternal(v): Poistaa lehden v ja tämän vanhemman u sekä sijoittaa v:n sisaruksen u:n paikalle (kuva 5.9.). Operaatio palauttaa solmun u. Virhe esiintyy, mikäli v on sisäsolmu. Syöte: paikka Tulos: alkio Oletetaan käytettävissä olevan binääripuukonstruktori, joka palauttaa yksittäisen lehden ilman alkiota mukanaan. Tästä solmusta lähtien voidaan muodostaa binääripuita metodilla expandexternal. Vastaavasti binääripuita voidaan karsia metodia removeaboveexternal käyttäen ja lopulta päätyä yksittäiseen solmuun. Muuntyyppisiä hyödyllisiä binääripuumetodeja käsitellään 7. luvusssa. 5. kappale 207 Mallinnetaan binääripuuta abstraktina tietotyyppinä koodin 5.9. Java-rajapinnalla, joka laajentaa edeltävää SimpleTreerajapintaa. Tältä yliluokaltaan se perii metodit. Kun binääripuut ovat järjestettyjä solmun vasen lapsi on järjestyksessä ennen oikeaa. public interface BinaryTree extends SimpleTree { // Simplified interface for a binary tree whose nodes (positions) // store arbitrary elements. // Accessor methods: public Position leftchild(position v); public Position rightchild(position v); public Position sibling(position v); Koodi 5.9. (alku) Rajapinta BinaryTree, joka laajentaa rajapintaa SimpleTree. 5. kappale 208

// Update methods: public void expandexternal(position v); public Object removeaboveexternal(position v); Koodi 5.9. (loppu) Rajapinta BinaryTree. Seuraavat oletukset ovat tehtävissä rajapinnan BinaryTree toteuttavan luokan metodeista. Kaikki rajapinnan SimpleTree yhteydessä esitetyt aikakompleksisuudet pysyvät sellaisenaan. Metodi children(v) toimii binääripuille ajassa, koska jokaisella solmulla on joko ei yhtään tai kaksi lasta. Metodit leftchild(v) ja rightchild(v) toimivat ajassa. Metodit expandexternal(v) ja removeaboveexternal(v) toimivat ajassa. Binääripuussa kulkeminen Puiden soveltaminen merkitsee hyvin usein niiden läpikäyntiä. Seuraavaksi tarkastellaan mainittua asiaa hyödyntäen annettua rajapintaa BinaryTree. Binääripuun kulkeminen esijärjestyksessä Mikä tahansa binääripuu voidaan mieltää myös yleisenä puuna, joten yleisen puun esijärjestys toimii yhtä hyvin binääripuulle. Tällöin sitä (koodi 5.10.) voidaan jopa yksinkertaistaa, sillä binääripuu on rajoitetumpi kuin yleinen muoto. Jälleen puun kulkemisella esijärjestyksessä on lukuisia sovelluksia. Esimerkkinä voidaan kloonata, kopioida, binääripuu T samanlaiseksi puuksi T aloittaen yhden lehden käsittävästä puusta T ja käyden puuta T rekursiivisesti läpi (koodi 5.11.). 5. kappale 209 5. kappale 210 Algorithm binarypreorder(t,v): käy solmussa v (ja tee siellä tarvittava laskenta) if v on sisäsolmu then binarypreorder(t,t.leftchild(v)) {käy rekursiivisesti vasemmassa alipuussa binarypreorder(t,t.rightchild(v)) {käy rekursiivisesti oikeassa alipuussa Koodi 5.10. Algoritmi binarypreorder. Kun solmussa käyminen vaatii ajan, binääripuun kopioiminen tarvitsee vain ajan O(n). Algorithm clone(t,t,v,v ): Input: Binääripuu T, joka sisältää solmun v, ja binääripuu T, joka sisältää lehtisolmun v. Output: Lisätty binääripuu T niin, että v on alipuun juurena ja alipuu on kopio puusta T, jonka juurena v on. if v on sisäsolmu then expandexternal(v ) v.element v.element() clone(t,t,t.leftchild(v),t.leftchild(v )) clone(t,t,t.rightchild(v),t.rightchild(v )) Koodi 5.11. Algoritmi clone. 5. kappale 211 5. kappale 212

Binääripuun kulkeminen jälkijärjestyksessä Jälkijärjestys toimii binääripuilla samantapaisesti kuin edellä. Algoritmi on annettu koodina 5.12. Algorithm binarypostorder(t,v): if v on sisäsolmu then binarypostorder(t,t.leftchild(v)) binarypostorder(t,t.rightchild(v)) käy solmussa v (ja tee siellä tarvittava laskenta) Koodi 5.12. Algoritmi binarypostorder. Jälkijärjestyskäsittely sopii hyvin mm. aritmeettisten binääristen operaatioiden evaluointiin ja muihin alhaalta-ylös- (bottom-up) eli kokoaviin evaluaatio-ongelmiin. Binääripuun käsittely jälkijärjestyksessä on tehtävissä ajassa O(n). Binääripuun kulkeminen välijärjestyksessä Binääripuut voidaan kulkea kolmannellakin tavalla, välijärjestyksessä (inorder). Tällöin solmussa käydään vasemman ja oikean alipuun rekursiivisten kutsujen välissä (koodi 5.13.). 5. kappale 213 5. kappale 214 Algorithm inorder(t,v): if v on sisäsolmu then inorder(t,t.leftchild(v)) {kulje vasen alipuu rekursiivisesti käy solmussa v (ja tee siellä tarvittava laskenta) if v on sisäsolmu then inorder(t,t.rightchild(v)) {kulje oikea alipuu rekursiivisesti / + + 6 Koodi 5.13. Binääripuun kulkeminen välijärjestyksessä. + 3 2 3 Binääripuun T kulkeminen välijärjestyksessä on nähtävissä solmujen läpikäyntinä vasemmalta oikealle. Jokaiselle solmulle v vasta kaikkien sen vasemman alipuun solmujen käynnin jälkeen käydään v:ssä ja vasta tämän jälkeen sen oikean alipuun solmuissa (kuva 5.11). 3 1 9 5 Kuva 5.11. Binääripuun kulkeminen välijärjestyksessä. 7 4 5. kappale 215 5. kappale 216

Myös välijärjestyskulkemisella on lukuisia sovelluksia. Yksi tärkeimmistä on järjestetyn sekvenssin alkioiden tallettaminen puuhun, joka määrittelee rakenteen nimeltä binäärihakupuu (binary search tree) (kuva 5.12.). Siinä jokainen solmu v tallettaa alkion e tavalla, jossa v:n vasempaan alipuuhun talletetut alkiot ovat pienempiä tai yhtä suuria kuin e ja oikeaan alipuuhun talletetut tätä suurempia. Kuljettaessa välijärjestyksessä alkiot käydään ne läpi ei-vähenevässä järjestyksessä. Binäärinen hakupuu voidaan mieltää binääriseksi päätöspuuksi, joka tukee hakua ja jossa päätös tehdään sisäsolmussa kysymykselle, onko solmun alkio joko pienempi tai yhtä suuri tai suurempi kuin haettava alkio. 5. kappale 217 Binääripuuta käytetään paikallistamaan määrätynarvoinen alkio kulkien puuta T alaspäin. Kussakin sisäsolmussa verrataan sen arvoa haettavaan alkioon (hakuavaimeen). Mikäli vastaus on pienempi, jatketaan hakua vasemmassa alipuussa. Jos vastaus on yhtä suuri, alkio on löydetty. Mikäli se on suurempi, jatketaan hakua oikeassa alipuussa. Jos törmätään lehteen alkiota löytämättä, haku päättyy. Tärkeä havainto on, että haun suoritusaika on suhteessa puun T korkeuteen. Kuva 5.12. esittää esimerkin binäärihausta, mutta tätä tarkempi käsittely annetaan myöhemin, luvussa 7.3. Puiden kulkemisalgoritmien tehokkuus perustuu rekursiivisyyteen. Rekursion käyttö ei kuitenkaan ole välttämätöntä, vaan voidaan käyttää (eksplisiittistä) pinoa, johon talletetaan myöhemmin tarvittavia esivanhempisolmuja matkan varrelta näihin myöhemmin palaamista varten. Tämä ei-rekursiivinenkin käsittely voidaan suorittaa lineaarisessa ajassa ja samalla säästää hieman tarvittavaa muistitilaa, jota rekursiokutsujen hallinta vaatii ohjelmointiympäristöltä. 5. kappale 218 Binääripuun Eulerin matkan kulkeminen 31 25 42 62 58 90 Edellä esitetyissä puunkulkemismenetelmissä käytiin kussakin solmussa tarkalleen vain kerran. Nämä menetelmät voidaan yhdistää yhdeksi menetelmäksi sallimalla yhtä useampi käynti solmussa puun kulkemisen aikana. Tällainen menetelmä on Eulerin matkan kulkeminen (Euler tour traversal). Siinä saadaan puun yleisen kulkemisen esitys. 12 36 75 Kuva 5.12. Kokonaislukuja sisältävä binäärihakupuu. Kiinteät paksut viivat (keskellä) kuvaavat luvun 36 onnistuneen haun ja katkoviivat luvun 70 epäonnistuneen haun. 5. kappale 219 Intuitiivisesti esitettynä kysymys on binääripuun kulkemisesta ympärikävelynä, jossa lähdetään juuresta ja kuljetaan tämän vasempaan lapseen pitämällä kaaret (edge, arc) kulkusuunnasta katsoen vasemmalla puolella (kuva 5.13.). Puun T jokaisessa solmussa v käydään kolmesti: vasemmalta (ennen solmun v vasempaan alipuuhun menoa), alhaalta (solmun v vasemman ja oikean alipuun välissä) ja oikealta (solmun v oikean alipuun käynnin jälkeen). 5. kappale 220

Solmun v ollessa lehti kaikki kolme käyntiä tapahtuvat itse asiassa samalla kertaa. Menetelmä on esitetty algoritmina koodissa 5.14. / + + + 3 2 3 3 1 9 5 7 4 Kuva 5.13. Eulerin matka binääripuussa. 5. kappale 221 6 Algorithm eulertour(t,v): käy solmussa v vasemmalta if v on sisäsolmu then käy rekursiivisesti solmun v vasemmassa alipuussa käyttäen kutsua eulertour(t,t.leftchild(v)) käy solmussa v alhaaltapäin if v on sisäsolmu then käy rekursiivisesti solmun v oikeassa alipuussa käyttäen kutsua eulertour(t,t.rightchild(v)) käy solmussa v oikealta Koodi 5.14. Algoritmi eulertour. 5. kappale 222 Kulkeminen esijärjestyksessä muistuttaa Eulerin matkaa siinä mielessä, että solmuun liittyy käynti tultaessa solmuun sen vasemmalta puolelta. Vastaavasti väli- ja jälkijärjestyksille solmussa käydään tultaessa alhaaltapäin tai oikealta. Esi-, jälki- ja välijärjestys-algoritmien suoritusajat olivat O(n), kun käynti solmussa vaati vakioajan. Niin on Eulerin matkan tapauksessakin, suoritusaika O(n). Eulerin matkalla on erilaisia sovelluksia, joihin ei tässä puututa. Kuvatusta yleisluonteisesta Eulerin matkasta voidaan muodostaa myös muita kulkemismenetelmiä kuin em. kolme perusmenetelmää. 5.4. Puutietorakenteiden toteuttaminen Seuraavaksi tarkastellaan puiden konkreettista esittämistä tietorakenteina sekä puun ja binääripuun abstrakteja tietotyyppejä. Binääripuiden sekvenssipohjainen rakenne Binääripuun T yksinkertainen toteutus perustuu sen solmujen määrättyyn numerointiin. Olkoon p(v) kuhunkin solmuun v liitetty kokonaisluku määriteltynä seuraavasti (kuva 5.14.): Jos v on puun T juuri, niin p(v) = 1. Jos v on solmun u vasen lapsi, niin p(v) = 2p(u). Jos v on solmun u oikea lapsi, niin p(v) = 2p(u)+1. 5. kappale 223 5. kappale 224

1 1 2 3 2 3 / + 4 8 9 5 10 11 12 6 13 14 7 15 4 5 6 + 8 9 10 11 12 13 6 7 + 3 2 3 (a) 16 17 20 21 26 27 3 1 9 5 7 4 Kuva 5.14. Binääripuun tasojen numerointi: (a) yleinen kaavio. Kuva 5.14. (jatkoa) Binääripuun tasojen numerointi: (b) esimerkki. 5. kappale 225 5. kappale 226 Funktio p antaa solmujen järjestysnumeroinnin, kun se järjestää solmut puussa tasoittain kasvavaan järjestykseen vasemmalta oikealle (joskus voidaan hypätä yli joitakin numeroita, so. tyhjät alkiot). Funktio p muodostaa binääripuun esityksen sekvenssin S avulla, jolloin T:n solmu v liitetään S:n alkion astetta p(v) (ks. kuvaa 5.15.). Tavallisesti sekvenssi tässä tapauksessa toteutetaan taulukkona, jonka käyttö on nopeaa ja yksinkertaista. Sille saadaan tehokkaat toteutukset metodeille root, parent, leftchild, rightchild, isinternal, isexternal ja isroot muutamin yksinkertaisin aritmeettisin laskutoimituksin solmujen luvuilla p. Yksityiskohtiin menemättä (harjoitustehtävä) todetaan jokaisen mainituista metodeista toimivan ajassa. S 8 2 + 4 5 + 3 9 3 1 9 5 1 / 6 3 12 13 0 1 2 3 4 5 6 7 8 9 10 11 12 13 Kuva 5.15. Binääripuuesitys sekvenssin avulla. T 2 7 5. kappale 227 5. kappale 228

Olkoon n puun T solmujen lukumäärä ja N arvon p(v) maksimi puussa T. Kun sekvenssi on toteutettu em. tavalla, taulukon koko on vähintään n+1 ja enintään N (alkio astetta 0 ei liity mihinkään solmuun). Sekvenssi voi sisältää paljon tyhjiä alkioita, jotka eivät liity mihinkään solmuun. Pahimmassa tapauksessa on N = 2 (n+1)/2-1 (perustelu harjoitustehtävänä). Myöhemmin, luvussa 6.3., esitetään keko- eli kasarakenne, jossa on N = n+1. Tilaa hukkaavasta piirteestään huolimatta sekvenssi on tehokas rakenne binääripuiden tapauksessa, mutta ei enää monihaaraisen yleisen puun tilanteessa. Taulukossa 5.1. on yhteenveto binääripuutoteutuksen metodien suoritusajoista sekvenssiä käytettäessä. Sekvenssi on tässä tehokas, mutta korkeille puille muistitilan käyttö on tehotonta. 5. kappale 229 Taulukko 5.1. Binääripuun T metodien suoritusajat käytettäessä taulukolla toteutettua sekvenssiä. Solmujen määrä on n ja sekvenssin koko N. Metodeihin elements(), positions(), ja children(v) liittyvät hasmoreelements() ja nextelement() toimivat ajassa. Tilavaatimus on O(N), joka on O(2 (n+1)/2 ) pahimmassa tapauksessa. Metodilla removeaboveexternal(v) on yleisessä tapauksessa O(n), mutta solmun v molempien lapsien ollessa lehtiä, kuten yleensä, se on. operaatio suoritusaika size, isempty positions, elements swap, replace(v,e) root, parent, children leftchild, rightchild isinternal, isexternal, isroot expandexternal removeaboveexternal O(n) O(n), 5. kappale 230 Binääripuiden linkitetty rakenne vanhempi juuri 5 koko Linkitetty rakenne (linked structure) on luonnollinen tapa toteuttaa binääripuu T. Tällöin jokainen solmu v esitetään oliolla, jolla on viittaukset solmuun v talletettuun alkioon sekä solmun v lapsiin ja vanhempaan liittyviin olioihin (ks. kuva 5.16.). Luokka Node (koodi 5.15.) esittää solmun v olion avulla, jolla on muuttujat element, left, right ja parent. Nämä viittaavat solmuun v tallettettuun alkioon, tämän vanhempaan sekä vasempaan ja oikeaan lapseen. Luokalla Node on myös metodit, joilla muuttujat saadaan ja asetetaan. 5. kappale 231 vasen alkio oikea (a) Baltimore Chicago New York Providence Seattle (b) Kuva 5.16. Binääripuutoteutus linkitettynä rakenteena: (a) solmun olio ja (b) viiden solmun binääripuu kokonaisuudessaan. 5. kappale 232

public class Node implements Position { // node of a binary tree realized by means of a linked structure private Container container; // container storing this node private Object element; // element stored at the node private Node left; // left child private Node right; // right child private Node parent; parent node public Node() { // default constructor Koodi 5.15. (alkuosa) Solmun luokka Node. public Node(Object o, Node u, Node v, Node w, Container c) { // constructor with parameters setelement(o); setcontainer(c); setparent(u); setleft(v); setright(w); public Object element() { return element; protected void setelement(object o) { element=o; Koodi 5.15. (jatkoa) Solmun luokka Node. 5. kappale 233 5. kappale 234 public Container container() { return container protected void setcontainer(container c) { container=c; protected Node getleft() { return left; protected void setleft(node v) { left=v; protected Node getright() { return right protected void setright(node v) { right=v; protected Node getparent() { return parent protected void setparent(node v) { parent=v; Koodi 5.15. (loppuosa) Solmun luokka Node. 5. kappale 235 Luokka LinkedBinaryTree (koodi 5.16.) tallettaa muuttujiinsa puun koon ja viittauksen olioon Node, joka liittyy puun juureen. Se toteuttaa rajapinnan BinaryTree (koodi 5.9.) metodit replace, root, parent, leftchild, rightchild, isinternal, isexternal, isroot ja expandexternal kutsuen suoraan luokan Node (koodi 5.15.) metodeja. Jokainen näistä metodeista toimii ajassa. public class LinkedBinaryTree implements BinaryTree { private Position root; // reference to the root private int size; // number of nodes public LinkedBinaryTree() { root = new Node(null,null,null,null,this); size = 1; Koodi 5.16 (alkuosa) Osa luokkaa LinkedBinaryTree, jossa on muutamien metodien toteutuksia mukana rajapinnasta BinaryTree. 5. kappale 236

public int size() { return size; public boolean isempty() { return (size==0); public Object replace(position v, Object o) { Object temp = ((Node) v).element(); ((Node) v).setelement(o); return temp; public Position leftchild(position v) { return ((Node) v).getleft(); public Position rightchild(position v) { return ((Node) v).getright(); public Position parent(position v) {return ((Node) v).getparent(); public boolean isinternal(position v) { return (((Node) v).getleft()!=null && ((Node) v).getright()!=null); public boolean isexternal(position v) {return (((Node) v).getleft()==null && ((Node) v).getright()==null); public boolean isroot(position v) { return (v==root()); public Position root() { return root; public void expandexternal(position v) { if (isexternal(v)) { ((Node) v).setleft(new Node(null,(Node) v,null,null,this)); ((Node) v).setright(new Node(null,(Node) v,null,null,this)); size+=2; Koodi 5.16 (jatkoa) Osa luokkaa LinkedBinaryTree, jossa on muutamien metodien toteutuksia mukana rajapinnasta Koodi 5.16 (loppuosa) Osa luokkaa LinkedBinaryTree, jossa on muutamien metodien toteutuksia mukana rajapinnasta BinaryTree. BinaryTree. 5. kappale 237 5. kappale 238 Edeltä (koodi 5.16.) oli jätetty pois osa metodien toteutuksista. Nämäkin käydään läpi seuraavassa tarkastelussa. Metodit size() ja isempty() toimivat ajassa. Metodissa swap(v,w) yksinkertaisesti vaihdetaan alkioiden v ja w viittaukset keskenään. Se toimii ajassa. Myös metodi replace(v,e) on toteutettavissa toimivaksi ajassa. Käyttäen sekvenssiä ja paikkaa siinä operaatiot elements(), positions() ja children() ovat toteutettavissa. Niissä käytettävät metodit hasmoreelements() ja nextelement() toimivat ajassa. Metodi positions() voidaan toteuttaa kulkemalla binääripuu läpi (esi-, väli- tai jälkijärjestyksessä). Käytäessä solmussa v lisätään viittaus siihen sekvenssin loppuun. Metodi elements() on samankaltainen. Tällöin nämä toimivat ajassa O(n). Luokalla LinkedBinaryTree (koodi 5.16.) on ilman parametreja oleva konstruktori, joka palauttaa yhden solmun käsittävän Jokaista puun T solmua varten on olio luokasta Node (koodi 5.15.). Puuta itseään varten on olio luokasta LinkedBinaryTree (koodi 5.16.). Nämä oliot vaativat vakiomäärän tilaa. Niinpä tarvittava kokonaistila on c 1 n + c 2, missä c 1 ja c 2 ovat joitakin positiivisia vakioita, ts. O(n). Taulukossa 5.2. on yhteenveto binääripuun metodien suoritusaikavaatimuksista. Taulukko 5.2. Binääripuun (n solmua) metodien suoritusajat linkitettyä rakennetta käytettäessä. Muistitilavaatimus on O(n). operaatiot size, isempty positions, elements swap, replace root, parent, children(v,e) leftchild, rightchild isinternal, isexternal, isroot expandexternal, suoritusaika O(n) puun. 5. kappale 239 removeaboveexternal 5. kappale 240

vanhempi Yleisten puiden linkitetty rakenne alkio New York Linkitetyn rakenteen käyttö on mahdollista laajentaa binääripuista yleisiin puihin. Kun ei ole rajoitettu lasten määrää yleisen puun solmuissa, käytetään säiliötä (container, esim. sekvenssinä) solmun v lasten tallettamiseen muuttujien sijasta. Tätä esittää kuva 5.17. Käytettäessä säiliötä lasten tallettamiseen metodi children(v) voidaan toteuttaa yksinkertaisesti käyttäen apuna metodia elements(). 5. kappale 241 lasten säiliö (a) Baltimore Chicago Providence Seattle Kuva 5.17. Yleisen puun linkitetty rakenne: (a) solmuun liitetty olio ja (b) osa tietorakennetta, jossa on solmu lapsineen. (b) 5. kappale 242 Taulukko 5.3. esittää linkitetyn rakenteen toteutusten suorituskyvyn. Operaatioiden analyysi jääköön lukijalle. Taulukko 5.3. Yleisen puun (n solmua ja c v solmun v lapset) metodien suoritusajat käytettäessä linkitettyä rakennetta ja tilavaatimuksen ollessa O(n). operaatio size, isempty positions, elements swap, replace(v,e) root, parent children(v) O(c v ) isinternal, isexternal, isroot suoritusaika O(n) 5. kappale 243 Yleisten puiden esittäminen binääripuiden avulla Yleisen puun T vaihtoehtoinen esitystapa edellä esitetylle on muuntaa se binääripuuksi T (kuva 5.18.). Puu T on järjestetty tai mielivaltaisessa järjestyksessä. Muunnos on seuraava: Puun T jokaisella solmulla u on olemassa puun T sisäsolmu u, joka liittyy solmuun u. Jos puun T solmu u on lehti, jolla ei ole sitä seuraavia sisaruksia, niin puun T solmun u lapset ovat lehtiä. Jos puun T solmu u on sisäsolmu ja v on u:n ensimmäinen lapsi, niin v on u :n vasen lapsi puussa T. Jos solmulla v on sisarus w välittömästi seuraavana, niin w on v :n oikea lapsi T :ssa. Puun T lehdet eivät liity puun T solmuihin, vaan ovat ainoastaan paikanpitäjiä. 5. kappale 244

A A B B C D E F G (a) E F C G D On helppoa ylläpitää puiden T ja T vastaavuus sekä ilmaista operaatiot T:ssä vastaavien operaatioiden suhteen T :ssa. Voidaan ajatella muunnos puusta T puuhun T niin, että otetaan kukin joukko {v 1, v 2,, v k sisaruksia puusta T vanhempanaan v ja korvataan se oikeanpuoleisten lapsien ketjulla, jonka juuri on solmussa v 1. Tästä puolestaan tulee solmun v vasen lapsi. Kuva 5.18. Yleisen puun esitys binääripuun avulla: (a) puu T ja (b) edelliseen liittyvä puu T. Katkoviivat yhdistävät T :n solmut, jotka liittyvät T:n sisarussolmuihin. (b) 5. kappale 245 Taulukossa 5.4. on yleisen puun metodien suoritusajat käytettäessä kuvatulla tavalla binääripuuta sen toteutuksena. 5. kappale 246 Taulukko 5.4. Binääripuun (linkitetty rakenne) avulla toteutetun puun metodien suoritusajat, missä puun solmujen määrä on n, solmun v lasten määrä c v ja sisarusten määrä s v. Metodien elements(), positions() ja children(v) käyttämät metodit hasmoreelements() ja nextelement() vaativat ajan. Tilavaatimus on O(n). operaatio suoritusaika size, isempty positions, elements O(n) swap, replace(v,e) root parent(v) O(s v ) children(v) O(c v ) isinternal, isexternal, isroot 5. kappale 247