8. Puna-mustat puut ja tietorakenteiden täydentäminen

Samankaltaiset tiedostot
811312A Tietorakenteet ja algoritmit IV Perustietorakenteet

4. Perustietorakenteet

Hakupuut. tässä luvussa tarkastelemme puita tiedon tallennusrakenteina

811312A Tietorakenteet ja algoritmit V Hash-taulukot ja binääriset etsintäpuut

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

Algoritmit 1. Luento 7 Ti Timo Männikkö

811312A Tietorakenteet ja algoritmit, , Harjoitus 5, Ratkaisu

Algoritmit 2. Luento 2 To Timo Männikkö

811312A Tietorakenteet ja algoritmit, , Harjoitus 5, Ratkaisu

Algoritmit 2. Luento 2 Ke Timo Männikkö

Algoritmit 2. Luento 5 Ti 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.

Kierros 4: Binäärihakupuut

CS-A1140 Tietorakenteet ja algoritmit

Algoritmit 2. Luento 6 To Timo Männikkö

14 Tasapainotetut puurakenteet

Algoritmit 2. Luento 5 Ti Timo Männikkö

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

TKT20001 Tietorakenteet ja algoritmit Erilliskoe , malliratkaisut (Jyrki Kivinen)

Binäärihaun vertailujärjestys

5. Hash-taulut ja binääriset etsintäpuut

Algoritmit 2. Luento 6 Ke Timo Männikkö

1.1 Tavallinen binäärihakupuu

Tietorakenteet, laskuharjoitus 7, ratkaisuja

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ö

811312A Tietorakenteet ja algoritmit , Harjoitus 2 ratkaisu

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

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

A TIETORAKENTEET JA ALGORITMIT

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

811312A Tietorakenteet ja algoritmit, , Harjoitus 7, ratkaisu

Algoritmi on periaatteellisella tasolla seuraava:

f(n) = Ω(g(n)) jos ja vain jos g(n) = O(f(n))

811312A Tietorakenteet ja algoritmit V Verkkojen algoritmeja Osa 2 : Kruskalin ja Dijkstran algoritmit

Algoritmit 1. Luento 5 Ti Timo Männikkö

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

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

58131 Tietorakenteet ja algoritmit Uusinta- ja erilliskoe ratkaisuja (Jyrki Kivinen)

811312A Tietorakenteet ja algoritmit III Lajittelualgoritmeista

811312A Tietorakenteet ja algoritmit Kertausta jälkiosasta

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

Tarkennamme geneeristä painamiskorotusalgoritmia

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

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

811312A Tietorakenteet ja algoritmit II Perustietorakenteet

Algoritmit 1. Luento 6 Ke Timo Männikkö

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Algoritmit 1. Luento 8 Ke Timo Männikkö

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

B + -puut. Kerttu Pollari-Malmi

Algoritmit 1. Luento 12 Ti Timo Männikkö

Olkoon seuraavaksi G 2 sellainen tasan n solmua sisältävä suunnattu verkko,

Luku 8. Aluekyselyt. 8.1 Summataulukko

Datatähti 2019 loppu

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

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

2. Perustietorakenteet

4 Tehokkuus ja algoritmien suunnittelu

Algoritmit 1. Luento 12 Ke Timo Männikkö

Algoritmit 2. Luento 4 Ke Timo Männikkö

811312A Tietorakenteet ja algoritmit I Johdanto

811312A Tietorakenteet ja algoritmit, , Harjoitus 3, Ratkaisu

Johdatus graafiteoriaan

Algoritmit 2. Luento 9 Ti Timo Männikkö

7. Tasapainoitetut hakupuut

Algoritmit 2. Luento 7 Ti Timo Männikkö

4. Joukkojen käsittely

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

verkkojen G ja H välinen isomorfismi. Nyt kuvaus f on bijektio, joka säilyttää kyseisissä verkoissa esiintyvät särmät, joten pari

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

Algoritmit 2. Luento 10 To Timo Männikkö

1 Puu, Keko ja Prioriteettijono

13 Lyhimmät painotetut polut

Algoritmit 2. Luento 3 Ti Timo Männikkö

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

Luku 4. Tietorakenteet funktio-ohjelmoinnissa. 4.1 Äärelliset kuvaukset

9.3 Algoritmin valinta

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 ja algoritmit

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Tietorakenteet, laskuharjoitus 6,

58131 Tietorakenteet Erilliskoe , ratkaisuja (Jyrki Kivinen)

Tietorakenteet ja algoritmit - syksy

Algoritmit 2. Luento 3 Ti Timo Männikkö

Algoritmit 1. Luento 13 Ma Timo Männikkö

Algoritmit 2. Demot Timo Männikkö

T Syksy 2004 Logiikka tietotekniikassa: perusteet Laskuharjoitus 7 (opetusmoniste, kappaleet )

Tietorakenteet, laskuharjoitus 3, ratkaisuja

ja λ 2 = 2x 1r 0 x 2 + 2x 1r 0 x 2

811312A Tietorakenteet ja algoritmit Kertausta jälkiosasta

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

V. V. Vazirani: Approximation Algorithms, luvut 3-4 Matti Kääriäinen

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

Algoritmit 1. Demot Timo Männikkö

isomeerejä yhteensä yhdeksän kappaletta.

Kannan vektorit siis virittävät aliavaruuden, ja lisäksi kanta on vapaa. Lauseesta 7.6 saadaan seuraava hyvin käyttökelpoinen tulos:

Valitaan alkio x 1 A B ja merkitään A 1 = A { x 1 }. Perinnöllisyyden nojalla A 1 I.

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

10. Painotetut graafit

Transkriptio:

8. Puna-mustat puut ja tietorakenteiden täydentäminen Tässä osassa perehdytään puna-mustiin puihin, jotka toteuttavat yhden tavan pitää binäärinen hakupuu tasapainossa. Teoksessa [Cor] käsitellään puna-mustia puita luvussa 13. Tarkastelun lopuksi käsitellään vielä tietorakenteiden täydentämistä, johon perehdytään myös kirjan [Cor] luvussa 14. Tämän materiaalin osaamista ei vaadita kurssin tenteissä. 8.1. Puna-mustat puut Kaikkien edellä esitettyjen binääristen etsintäpuiden algoritmien aikakompleksisuus on luokkaa (h), kun h on käsiteltävän puun korkeus. Tällaisessa puussa voi olla 2 h 1solmua, joten puun ollessa tasapainossa (mikään alipuu ei ole muita alipuita selvästi korkeampi), algoritmien kompleksisuus on luokkaa (lg(n )), kun syötteen koon mittana käytetään avaimien lukumäärää n. Valitettavasti lisäys- ja poistoalgoritmit eivät välttämättä pidä puuta tasapainossa, vaan tuloksena syntynyt puu riippuu avaimien järjestyksestä. Esimerkiksi lisättäessä avaimet suuruusjärjestyksessä puuhun muodostuu vain yksi haara. Operaatioiden lukumäärä voi siis huonoimmassa tapauksessa riippua lineaarisesti avaimien lukumäärästä. Tämän vuoksi on kehitetty useita etsintäpuumalleja, joissa taataan puun tasapainon säilyminen ja siten logaritminen käsittelyaika kaikissa tapauksissa. Ensimmäinen tällainen etsintäpuun malli on vuonna 1962 esitelty AVL-puu, joka on saanut nimensä keksijöistään (Adelson-Velskii ja Landis). Näihin tietorakenteisiin voi tutustua mm. teoksen [Wei] luvusta 4.4. Tässä käsitellään tarkemmin puna-mustia puita (red-black trees), jotka ovat yksi tapa tasapainottaa binäärisiä etsintäpuita. Kirjan [Cor] luvusta 13 voi myös perehtyä aiheeseen. Puna-mustat puut ovat binäärisiä hakupuita, joiden solmuihin on lisätty väri: solmut ovat joko punaisia tai mustia. Binäärinen hakupuu on puna-musta puu, jos se toteuttaa seuraavat viisi ehtoa: 1. Jokainen solmu on joko punainen tai musta. 2. Juuri on musta. 3. Jokainen lehti (NIL) on musta. 4. Jos solmu on punainen, sen molemmat lapset ovat mustia. 5. Kaikille solmuille on voimassa: Jokainen polku solmusta sen jälkeläislehtiin sisältää saman lukumäärän mustia solmuja. HUOM! Puun lehdet ovat siis aina tyhjiä (NIL) ja ne merkitään aina mustiksi. Niitä ei välttämättä merkitä kuvioissa näkyviin. Kaikilla sisäsolmuilla on siis aina kaksi lasta, joista toinen tai molemmat voivat olla lehtiä. Juuren vanhemmaksi merkitään NIL, joka on siis musta.

20 20 10 25 10 25 8 16 28 8 16 28 14 18 27 30 14 27 30 Kuva 8.1. Kaksi binääristä etsintäpuuta, jotka eivät ole puna-mustia puita. Kuvasta on jätetty merkitsemättä mustat tyhjät lehtisolmut. Samoin juuren vanhempi. Edellä olevat kaksi puuta eivät kumpikaan ole hyvin muodostettuja puna-mustia puita. Vasemmanpuoleisessa puussa punaisella solmulla 10 on punainen lapsi 16, joten ominaisuus 4 ei ole voimassa. Oikeanpuoleisessa puussa taas solmun 28 vasemmanpuoleisessa polussa lehteen on yksi musta solmu enemmän kuin oikeanpuoleisessa solmussa, joten ominaisuus 5 ei toteudu. Seuraava puu on sen sijaan oikea puna-musta puu, kuten on helppo todeta. 20 10 25 8 16 24 28 14 Kuva 8.2. Esimerkki puna-mustasta puusta. Osoitetaan seuraavaksi, että puna-mustat puut ovat riittävän tasapainoisia toimiakseen hyvinä hakupuina. Käytetään seuraavaa merkintää: Kun x on puna-mustan puun solmu, sen mustakorkeus bh(x) on sen mustien jälkeläisten lukumäärä polussa solmusta x puun lehteen saakka. Huomaa, että ominaisuuden 5 perusteella on merkityksetöntä, mihin solmun x jälkeläislehteen menevää polkua tarkastellaan. Perustellaan seuraava ominaisuus: Olkoon x punamustan puun solmu. Tällöin alipuu, jonka juuri x on, sisältää vähintään 2 1solmua. Tämä bh( x) voidaan näyttää induktiivisesti solmun x korkeuden suhteen. Jos solmun x korkeus on 0, niin x on 0 bh( x) lehti, bh(x)=0 ja sen alipuussa on vähintään 0 2 1 2 1solmua, joten väite on voimassa. Olkoon sitten solmun x korkeus h > 0, jolloin solmu on sisäsolmu ja solmulla kaksi lasta. Oletetaan, että väite on voimassa solmuille, joiden korkeus on pienempi. Nyt solmun x lasten bh( x) 1 mustakorkeus on vähintään bh(x)-1 ja niiden alipuissa yhteensä vähintään 2*(2 1) solmua. bh( x) 1 bh( x) Näin ollen solmun x alipuussa on vähintään 2*(2 1) 1 2 1solmua. Tarkasteltava ominaisuus on siis aina voimassa. 18 27 30

Todetaan että edellä havaitusta ominaisuudesta seuraa puun korkeuden logaritminen suhde solmujen lukumäärään: Jos puna-mustassa puussa on n solmua, niin puun korkeus on korkeintaan 2*lg( n 1). Olkoon nimittäin puun korkeus h. Puna-mustan puun ominaisuuksista 3 ja 4 seuraa, että juuren mustakorkeus on vähintään h/2, koska polulla juuresta lehteen vähintään joka toinen solmu on musta ja lehdet ovat aina mustia. Näin ollen puun solmujen lukumäärä n toteuttaa ehdon n 2 h / 2 1, mistä saadaan h/ 2 lg( n 1) ja h 2*lg( n 1). Puna-mustiin puihin voidaan luonnollisesti soveltaa binäärisen hakupuun algoritmeja avaimen etsimiseen sekä minimi- ja maksimiarvojen hakemiseen (edellisen osan algoritmit BST_ETSI, BST_MINIMI ja BST_MAKSIMI). Koska kaikki nämä algoritmit ovat kompleksisuudeltaan luokkaa (h), missä h on puun korkeus, niin edellä olevasta tarkastelusta seuraa heti, että punamustalle puulle algoritmien kompleksisuus on (lg n), missä n on puussa olevien avainten lukumäärä. Sen sijaan edellisessä osassa esitettyjä algoritmeja avainten lisäämiseen poistamiseen ei voi sellaisenaan soveltaa, sillä ne eivät välttämättä säilytä puuta puna-mustana. Puna-mustien puiden kunnossapidon perusoperaatio on rotaatio eli pyöritys. Pyöritys voidaan tehdä solmun suhteen joko vasemmalle tai oikealle. Vasen rotaatio voidaan tehdä mille tahansa solmulle, jonka oikea lapsi ei ole lehti. Samoin oikea rotaatio voidaan tehdä mille tahansa solmulle, jonka vasen lapsi ei ole lehti. Seuraava kuva havainnollistaa tilannetta x RBT_VASEN_ROTAATIO(T,x) y a y RBT_OIKEA_ROTAATIO(T,y) x c b c a b Kuva 8.3. Vasen ja oikea rotaatio. Edellä olevassa kuvassa solmut x ja y voivat olla punaisia tai mustia; solmun väri ei muutu rotaatiossa. Symbolit a, b ja c tarkoittavat solmujen alipuita. Huomaa, miten alipuu b vaihtaa vanhempaansa rotaatiossa. Vasemman rotaation pseudokoodi on esitetty seuraavassa:

Syöte: Binäärinen etsintäpuu T ja solmu x, jonka suhteen kierretään. Oletus: x.right!= NIL Tulostus: Suorittaa puussa vasemman rotaation solmun x suhteen RBT_VASEN_ROTAATIO(T,x) 1. y = x.right 2. x.right = y.left // y:n vasen alipuu x:n oikeaksi alipuuksi 3. if y.left!= NIL 4. y.left.p = x 5. y.p = x.p // x:n vanhempi y:n vanhemmaksi 6. if x.p == NIL 7. T.root = y 8. else if x == x.p.left 9. x.p.left = y 10. else 11. x.p.right = y 12. y.left = x // x y:n vasemmaksi lapseksi 13. x.p = y 14. return Oikea rotaatio on symmetrinen Syöte: Binäärinen etsintäpuu T ja solmu x, jonka suhteen kierretään. Oletus: x.left!= NIL Tulostus: Suorittaa puussa oikean rotaation solmun x suhteen RBT_OIKEA_ROTAATIO(T,x) 1. y = x.left 2. x.left = y.right // y:n oikea alipuu x:n vasemmaksi alipuuksi 3. if y.right!= NIL 4. y.right.p = x 5. y.p = x.p // x:n vanhempi y:n vanhemmaksi 6. if x.p == NIL 7. T.root = y 8. else if x == x.p.right 9. x.p.right = y 10. else 11. x.p.left = y 12. y.right = x // x y:n oikeaksi lapseksi 13. x.p = y 14. return Rotaatiot ovat vakioaikaisia, koska niissä tehdään vakiomäärä osoittimien sijoituksia.

Perehdytään seuraavaksi siihen, miten puna-mustaan puuhun voidaan lisätä avain niin, että puu säilyy puna-mustana ja siten tasapainossa. Algoritmi lisää ensin avaimen kuten binääriseen etsintäpuuhun, mutta lisäämisen jälkeen kutsutaan vielä rutiinia, joka korjaa puun: Syöte: Puna-musta puu T ja solmu z, jonka avain on lisättävä avain. Tulostus: Lisää avaimen z.key puuhun niin, että T säilyy puna-mustana. RBT_LISÄÄ(T,z) 1. y = NIL 2. x = T.root 3. while x!= NIL 4. y = x 5. if z.key < x.key 6. x = x.left 7. else 8. x = x.right 9. z.p = y 10. if y == NIL 11. T.root = z 12. else if z.key < y.key 13. y.left = z 14. else 15. y.right = z 16. z.left = NIL 17. z.right = NIL 18. z.color = RED 19. RBT_LISÄYS_KORJAUS(T,z)

Huomaa, että lisättävä solmu väritetään punaiseksi. Solmun lisääminen voi luonnollisesti rikkoa puun puna-mustaominaisuuden, joten tarvitaan korjausrutiini: Syöte: Puu T ja solmu z, joka on lisätty T:hen. T on ollut puna-musta puu ennen z:n lisäystä. Tulostus: Korjaa T:n puna-mustaksi. RBT_LISÄYS_KORJAUS(T,z) 1. while z.p.color == RED 2. if z.p == z.p.p.left 3. L_KORJAA_VASEN(T,z) 4. else 5. L_KORJAA_OIKEA(T,z) 6. T.root.color = BLACK L_KORJAA_VASEN(T,z) 1. y = z.p.p.right 2. if y.color == RED 3. z.p.color = BLACK // tapaus 1 4. y.color = BLACK // tapaus 1 5. z.p.p.color = RED // tapaus 1 6. z = z.p.p // tapaus 1 7. else 8. if z == z.p.right 9. z = z.p // tapaus 2 10. RBT_VASEN_ROTAATIO(T,z) // tapaus 2 11. z.p.color = BLACK // tapaus 3 12. z.p.p.color = RED // tapaus 3 13. RBT_OIKEA_ROTAATIO(T, z.p.p) // tapaus 3 L_KORJAA_OIKEA(T,z) 1. y = z.p.p.left 2. if y.color == RED 3. z.p.color = BLACK // tapaus 1* 4. y.color = BLACK // tapaus 1* 5. z.p.p.color = RED // tapaus 1* 6. z = z.p.p // tapaus 1* 7. else 8. if z == z.p.left 9. z = z.p // tapaus 2* 10. RBT_OIKEA_ROTAATIO(T,z) // tapaus 2* 11. z.p.color = BLACK // tapaus 3* 12. z.p.p.color = RED // tapaus 3* 13. RBT_VASEN_ROTAATIO(T, z.p.p) // tapaus 3* Perustellaan seuraavaksi, miksi T korjaantuu edellä määritellyillä operaatioilla puna-mustaksi puuksi. Perustelu ei ole erityisen tarkka: yksityiskohtaisempaa käsittelyä kaipaava voi perehtyä kirjan [Cor] esitykseen. Tutkitaan ensin, mitkä puna-mustan puun ominaisuudet voivat rikkoutua solmua lisättäessä. Palautetaan mieleen, että lisättävä solmu on aina punainen. Kun solmu lisätään puuhun, ominaisuudet 1 ja 3 säilyvät, koska puuhun lisätään punainen solmu, jolla on kaksi mustaa (tyhjää) lehtisolmua. Lisättävä punainen solmu sijoitetaan mustan lehtisolmun tilalle, joten ominaisuus 5 pysyy myös voimassa. Jos punainen solmu lisätään juureksi, voi

ominaisuus 2 rikkoutua. Mikäli lisättävä punainen solmu sattuu punaisen solmun lapsisolmuksi, voi myös ominaisuus 4 lakata pätemästä. Perehdytään nyt algoritmiin. Todetaan aluksi, että jos solmu lisätään tyhjään puuhun (jolloin se menee juureen) ei korjauksessa tehdä muuta kuin juuren muuttaminen mustaksi. Tällöin puusta tulee hyvin muodostettu puna-musta puu. Oletetaan jatkossa, että solmua ei lisätä juureen. Tällöin juuri on musta, joten jos solmu lisätään juuren lapseksi, saadaan puna-musta puu ilman korjauksia. Näin ollen voidaan olettaa, että solmulla on alussa isovanhempi. Edelleen havaitaan, että while-silmukan jokaisen kierroksen alussa solmu z on punainen ja silmukkaa jatketaan vain jos solmun z vanhempi on punainen. Siten vanhempi ei ole juuri ja solmulla z on puussa isovanhempi. Lisäksi solmu z nousee puussa kohti juurta jokaisella kierroksella, joten algoritmi päättyy. Mikäli solmu nousee juureen asti, puun juuri on silmukan päättyessä punainen. Tämä korjataan algoritmin RBT_LISÄYS_KORJAUS rivillä 6, joten algoritmin päättyessä joka tapauksessa puun juuri on musta. Näin ollen ainoa puna-mustan puun ominaisuus, joka ei ehkä ole algoritmin päättyessä voimassa on ominaisuus 4. Kun solmu lisätään, ainoa vika puussa voi olla, että lisätyn solmun z vanhempi on punainen. Tutkitaan nyt mitä algoritmissa tapahtuu tällöin. Koska algoritmit L_KORJAA_VASEN ja L_KORJAA_OIKEA ovat toistensa peilikuvat, riittää tarkastella toista. Oletetaan siis, että solmun z vanhempi on oman vanhempansa vasemmassa alipuussa ja mennään suorittamaan algoritmia L_KORJAA_VASEN. Tarkastelu jakaantuu nyt kolmeen tapaukseen: 1. Solmu y on punainen. Tällöin solmun z vanhempi ja solmu y väritetään mustiksi ja solmun z isovanhempi punaiseksi. Solmu z nousee kaksi askelta puussa. Seuraava kuva havainnollistaa tilannetta. Operaation jälkeen ainoa mahdollinen vika muodostuneessa puussa on uuden z:n vanhemman punaisuus. Mikäli näin on, jatketaan silmukkaa. 20 10 25 20 10 25 y 8 16 28 8 16 z 28 14 18 y 14 18 12 z 12

2. Solmu y on musta ja z on oikea lapsi. Tällöin tehdään seuraavan kuvan mukainen vasen rotaatio, 20 10 25 y 16 20 25 y 8 16 z 28 z 10 18 28 14 18 8 14 12 12 joka vie tilanteen tapaukseen 3. Solmu y on musta ja z on vasen lapsi. Tällöin väritetään solmun z vanhempi mustaksi ja isovanhempi punaiseksi. Lisäksi tehdään oikea rotaatio solmun z isovanhemman suhteen seuraavan kuvan mukaisesti. Operaation jälkeen solmun z vanhempi on musta ja silmukka päättyy. 16 20 25 y z 10 16 20 z 10 18 28 8 12 14 18 25 28 8 14 12 Edellisestä tarkastelusta huomataan, että while-silmukkaa suoritettaessa ominaisuus 4 voi rikkoutua vain yhdessä kohdassa, kun z ja sen vanhempi on punainen. Silmukan päättyessä solmun z vanhempi on musta, joten silmukan päättyessä ominaisuus 4 on puussa voimassa. Koska juuri väritetään lopulta mustaksi, saadaan algoritmin päättyessä puna-musta puu. Selvitetään vielä lisäysalgoritmin aikakompleksisuus. Lisäyksen operaatioiden lukumäärä ennen korjausta on, kuten binäärisillä etsintäpuillakin, luokkaa O(h), missä h on puun korkeus. Puussahan edetään syvemmälle, kunnes kohdataan lehti, johon solmu voidaan lisätä. Rotaatiot

ovat vakioaikaisia, koska niissä tehdään vakiomäärä osoittimien sijoituksia. Näin ollen korjauksen aikakompleksisuus riippuu siitä, kuinka monta kertaa while-silmukkaa suoritetaan. Korjattava solmu nousee puussa jokaisella silmukan kierroksella ylöspäin, joten myös korjauksen aikakompleksisuus luokkaa O(h). Näin ollen koko lisäysoperaation aikakompleksisuus on luokkaa O(h). Puun korkeuden osoitettiin aiemmin olevan suuruusluokkaa lg(n), kun n on puun solmujen lukumäärä. Puna-mustaan puuhun lisäyksen aikakompleksisuus on siten kaikkiaan luokkaa O(lg(n)), missä n on puun solmujen lukumäärä. Perehdytään vielä solmun poistamiseen puna-mustasta puusta. Aluksi tarvitaan apualgoritmi, joka siirtää puussa alipuun toisen alipuun paikalle: Syöte: Puna-musta puu T ja sen solmu u sekä solmu v. Tulostus: Poistaa puusta u:n ja siitä lähtevän alipuun ja korvaa tämän solmulla v ja siitä lähtevällä alipuulla. RBT_TRANSPLANT(T,u,v) 1. if u.p == NIL 2. T.root = v 3. else 4. if u == u.p.left 5. u.p.left = v 6. else 7. u.p.right = v 8. v.p = u.p

Puna-mustasta puusta poistaminen muistuttaa binäärisestä etsintäpuusta poistamista, mutta nyt pitää luonnollisesti huolehtia myös puna-mustaominaisuuksien säilymisestä. Syöte: Puna-musta puu T ja sen solmu z. Tulostus: Poistaa puusta solmun z niin, että jäljelle jää puna-musta puu. RBT_POISTA(T,z) 1. y = z 2. y_orig_color = y.color 3. if z.left == NIL 4. x = z.right 5. RBT_TRANSPLANT(T,z,z.right) 6. else 7. if z.right == NIL 8. x = z.left 9. RBT_TRANSPLANT(T,z,z.left) 10. else 11. y = BST_MINIMI(z.right) 12. y_orig_color = y.color 13. x = y.right 14. if y.p == z 15. x.p = y 16. else 17. RBT_TRANSPLANT(T,y, y.right) 18. y.right = z.right 19. y.right.p = y 20. RBT_TRANSPLANT(T,z,y) 21. y.left = z.left 22. y.left.p = y 23. y.color = z.color 24. if y_orig_color == BLACK 25. RBT_POISTO_KORJAUS(T,x) Algoritmi tuhoaa solmun kuten binäärisestä etsintäpuusta, mutta puun kunnossa pitämiseksi joudutaan lisäämään hieman ominaisuuksia. Tarkastellaan ensin muuttujan y arvoa algoritmin päättyessä. Mikäli poistettavalla solmulla z ei ole kumpaakin alipuuta, y on poistettu solmu z. Jos taas solmulla z on kumpikin alipuu, y on algoritmin suorituksen loputtua solmu, joka tulee poistettavan solmun z paikalle. Tällöin solmu x puolestaan siirretään solmun y alkuperäiselle paikalle. Oletetaan, että solmun y alkuperäinen väri on punainen. Tarkastellaan aluksi ensimmäistä tapausta: y on poistettu solmu z. Poistettu solmu ei punaisena voinut olla juuri; siten puun juuri on algoritmin päättyessä edelleen musta. Punaisen solmun poistaminen ei myöskään voi aiheuttaa punaisen solmun lapsen muuttumista mustaksi eikä se vaikuta mustien solmujen lukumäärään poluissa lehtiin. Näin ollen puu säilyy puna-mustana eikä korjauksia tarvita. Siirrytään tapaukseen, jossa y tulee solmun z tilalle. Koska y saa värikseen poistettavan solmun värin, poisto ei vaikuta mustien solmujen lukumääriin poluissa. Jos y oli solmun z oikea lapsi, y korvaa solmun z ja mikään puna-mustan puun ominaisuus ei rikkoudu. Jos y ei ollut solmun z oikea lapsi, solmun y oikea lapsi x korvaa solmun y. Koska y oli punainen, solmun x on oltava musta. Näin ollen operaatio ei aiheuta punaisten solmujen joutumista punaisten solmujen lapsiksi ja jälleen puu säilyy puna-mustana.

Solmun poistaminen voi vahingoittaa puna-mustaa puuta siis vain, jos solmun y alkuperäinen väri on musta. Tällöin saatetaan tarvita korjausoperaatio. Puu voi vahingoittua kolmella eri tavalla: 1. Solmu y oli alun perin juuri, joka poistettiin ja sen punaisesta lapsesta tuli uusi juuri. 2. Solmu x on punainen, samoin kuin solmun y vanhempi. Tällöin punaiselle solmulle tulee punainen lapsi. 3. Kun solmua y siirretään puussa, voi jokin polku, jolla y aiemmin oli, menettää yhden mustan solmun. Tällöin ei siis puna-mustan puun ominaisuus 5 säily voimassa. Kaikki nämä tapaukset voidaan käsitellä seuraavalla algoritmilla: Syöte: Puu T ja solmu x. Tulostus: Korjaa T:n puna-mustaksi. RBT_POISTO_KORJAUS(T,x) 1. while x!= T.root AND x.color == BLACK 2. if x == x.p.left 3. P_KORJAA _VASEN(T,x) 4. else 5. P_KORJAA_OIKEA(T,x) 6. x.color = BLACK P_KORJAA_VASEN(T,x) 1. w = x.p.right 2. if w.color == RED 3. w.color = BLACK // tapaus 1 4. x.p.color = RED // tapaus 1 5. RBT_VASEN_ROTAATIO(T, x.p) // tapaus 1 6. w = x.p.right // tapaus 1 7. if w.left.color == BLACK AND w.right.color == BLACK 8. w.color = RED // tapaus 2 9. x = x.p // tapaus 2 10. else 11. if w.right.color == BLACK 12. w.left.color = BLACK // tapaus 3 13. w:color = RED // tapaus 3 14. RBT_OIKEA_ROTAATIO(T,w) // tapaus 3 15. w = x.p.right // tapaus 3 16. w.color = x.p.color // tapaus 4 17. x.p.color = BLACK // tapaus 4 18. w.right.color = BLACK // tapaus 4 19. RBT_VASEN_ROTAATIO(T,x.p) // tapaus 4 20. x = T.root // tapaus 4

P_KORJAA_OIKEA(T,x) 1. w = x.p.left 2. if w.color == RED 3. w.color = BLACK // tapaus 1* 4. x.p.color = RED // tapaus 1* 5. RBT_OIKEA_ROTAATIO(T, x.p) // tapaus 1* 6. w = x.p.left // tapaus 1* 7. if w.right.color == BLACK AND w.left.color == BLACK 8. w.color = RED // tapaus 2* 9. x = x.p // tapaus 2* 10. else 11. if w.left.color == BLACK 12. w.right.color = BLACK // tapaus 3* 13. w:color = RED // tapaus 3* 14. RBT_VASEN_ROTAATIO(T,w) // tapaus 3* 15. w = x.p.left // tapaus 3* 16. w.color = x.p.color // tapaus 4* 17. x.p.color = BLACK // tapaus 4* 18. w.left.color = BLACK // tapaus 4* 19. RBT_OIKEA_ROTAATIO(T,x.p) // tapaus 4* 20. x = T.root // tapaus 4* Tutkitaan ensin, miten ominaisuus 5 korjautuu edellä mainitulla rutiinilla. Oletetaan, että alun perin mustaa solmua y on siirretty niin, että solmun y esi-isistä lähtevät polut menettävät yhden mustan solmun. Koska solmu x siirtyy solmun y paikalle, voidaan asia korjata seuraavalla tempulla: Jos x oli punainen, väritetään se sekä punaiseksi että mustaksi, ts. x on väriltään punainen mutta lasketaan se mustaksi polkuja tarkasteltaessa. Jos taas x oli musta, sanotaan sitä kaksinkertaisesti mustaksi ja lasketaan se kahtena mustana poluissa. Näin mustien solmujen määrä poluissa säilyy muuttumattomana. Tällöin luonnollisesti rikkoutuu puna-mustien puiden ensimmäinen ominaisuus, koska puussa on solmuja, jotka eivät ole punaisia eivätkä mustia. Korjausoperaation while-silmukan ajatuksena on siirtää poistossa muodostunutta ylimääräistä mustaa puussa ylöspäin, kunnes jokin seuraavista tapahtuu: 1. Solmu x on punainen ja musta, jolloin silmukka loppuu ja solmu x väritetään mustaksi. 2. Solmu x on juuri, jolloin ylimääräinen musta poistetaan. Huomioidaan ensin, että niin kauan kuin ollaan while-silmukan sisällä, solmu x on kaksinkertaisesti musta, koska siinä vaiheessa, kun x on punainen ja musta, silmukka lopetetaan ja x väritetään varsinaiselta väriltään mustaksi. Tarkastellaan ainoastaan vasemmanpuoleista korjausrutiinia, koska toinen rutiini on tämän peilikuva ja toimii vastaavasti. Esitettävissä kuvissa harmaa solmu voi olla väriltään joko musta tai punainen. Aluksi rutiinissa solmu w osoittaa solmun x sisareen, jonka suhteen tarkastelu voidaan jakaa neljään tapaukseen:

1. Solmu w on punainen. Tällöin menetellään seuraavan kuvan mukaisesti ja päädytään johonkin myöhemmistä tapauksista. B D x A D w B E p q C E x A w C t u r s t u p q r s 2. Solmu w on musta ja sen molemmat lapset ovat mustia. Tällöin puu muuntuu seuraavasti. B x B x A D w A D p q C E p q C E r s t u r s t u Mikäli solmu B on punainen, silmukka päättyy ja B väritetään mustaksi. Jos B on musta, siitä tulee kaksinkertainen musta ja silmukkaa jatketaan. Tällöin kaksinkertainen musta on siirtynyt puussa ylöspäin. Huomaa, että tämä on ainoa tapaus, jossa while-silmukka voi jatkua. 3. Jos solmun w oikea lapsi on musta ja vasen punainen, muutetaan solmu w punaiseksi ja lapsi mustaksi sekä tehdään oikea rotaatio, kuten seuraavassa kuvassa, B B x A D w x A C w p q C E p q r D r s t u s E t u jolloin päädytään tapaukseen

4. Solmun w oikea lapsi on punainen. Seuraava kuva osoittaa, miten solmun x ylimääräinen musta poistuu ja silmukka lopetetaan. Huomaa, että solmu D väritetään samanväriseksi kuin B on vasemmalla, olipa se punainen tai musta. B D x A D w B E p q C E x A C t u r s t u p q r s x = T.root Korjausrutiinien jälkeen puun juuri säilyy mustana ja kaikkiin polkuihin jokaisesta solmusta lehtiin sisältyy yhtä monta mustaa solmua. Näin ollen puu säilyy puna-mustana. Edellä oleva tarkastelu on jokseenkin pintapuolinen; kiinnostunut lukija voi löytää yksityiskohtaisemman perustelun kirjasta [Cor].

8.2 Tietorakenteiden täydentäminen Usein tulee eteen tilanteita, joissa ongelman ratkaisuun riittää esitettyjen tietorakenteiden käyttö sellaisenaan. Usein kuitenkin tulee eteen myös tilanteita, joissa ne eivät ole riittäviä. Silloin on joko kehitettävä ratkaisuun soveltuva uusi tietorakenne (harvinaista) tai muokattava olemassaolevaa tietorakennetta lisätiedolla ja uusilla operaatioilla (yleistä). Ainoastaan harvoissa tapauksissa tarvitaan erillisen suunnitteluprosessin kautta täysin yksilöllinen tietorakenne. Olemassaolevan tietorakenteen täydentäminen on usein nopeampi ja vähemmän riskialtis prosessi. Tällaisen lisätiedon muokkaaminen rakenteeseen ei kuitenkaan välttämättä ole helppoa, sillä se voi vaatia muutoksia myös rakenteen alkuperäisiin operaatioihin. Tässä kappaleessa tarkastellaan kahta puna-mustiin puihin perustuvaa täydentämisesimerkkiä. Esimmäisessä esimerkissä puna-mustaa puuta täydennetään siten, että se tukee ns. järjestystunnuslukuihin perustuvia joukko-operaatioita, etenkin tietyn lukuarvon nopeaa löytämistä järjestetystä joukosta. Tämän jälkeen käydään läpi yleinen tietorakenteiden täydentämisen formalismi. Lisäksi esitetään tulos, joka suoraviivaistaa puna-mustien puiden täydentämistä. Lopuksi suunnitellaan puna-mustiin puihin perustuva tietorakenne erimittaisten intervallien (suljettu väli [t 1, t 2 ], t 2 t 1 ) tallentamiseen ja ylläpitämiseen. Määritelmä 8.2.1 Olkoon A järjestämätön joukko, jossa on n alkiota. Joukon A i:s järjestystunnusluku (ith order statistic) on joukon A i:nneksi pienin alkio. Alkion x A järjestysluku (rank) on kyseisen alkion paikka A:n järjestetyssä joukossa A. Kuvassa 8.2.1 on esimerkki järjestystunnusluvusta ja järjestysluvusta. Seuraavassa tarkastellaan järjestystunnuslukujen löytämistä järjestämättömästä joukosta. Mikä tahansa i:s järjestystunnusluku voidaan löytää ajassa O(n) käymällä koko joukko järjestelmällisesti läpi (osoita tämä). Seuraavassa kuitenkin täydennetään puna-mustaa puuta siten, että mille tahansa n alkiota sisältävälle dynaamiselle joukolle on mahdollista löytää i:s järjestystunnusluku ajassa O(lg n). Tällaista tietorakennetta kutsutaan järjestystunnuspuuksi (orderstatistic tree), jossa jokaiseen puna-mustan puun solmuun x on lisätty tieto x.size. Tämä tieto kertoo kyseisen solmun määrittämässä alipuussa (puu, jonka juuri solmu x on) sijaitsevien sisäsolmujen lukumäärän (mukaan lukien x itse). Asettamalla tyhjälle solmulle (solmu on N one) size = 0, jokainen ei-tyhjä solmu toteuttaa ehdon x.size = x.left.size + x.right.size + 1.

Kuva 8.2.1: Järjestystunnusluvut Kuvassa 8.2.2 on esimerkki järjestystunnuspuusta, jossa solmun x ylempi arvo esittää avainta x.key ja alempi muuttujaa x.size. Seuraavassa määritellään Kuva 8.2.2: Esimerkki järjestystunnuspuusta kaksi järjestystunnuspuuta käsittelevää algoritmia. 8.2.1 Tietyn järjestysluvun omaavan alkion etsintä Algoritmi OS SELECT(x, i) palauttaa osoittimen solmuun, joka sisältää i:nneksi pienimmän luvun solmun x määrittämässä alipuussa. Tätä algoritmia käyttämällä voidaan siis löytää i:s järjestystunnusluku kutsumalla OS SELECT(T.root, i).

OS_SELECT (x, i) 1. r = x. left. size + 1 2. if i == r 3. return x 4. else if i < r 5. return OS_SELECT (x.left,i) 6. else 7. return OS_SELECT (x. right,i - r) Rivillä 1 lasketaan solmun x järjestysluku kyseisen solmun määrittämässä alipuussa. Arvo x.lef t.size on solmun x vasemman alipuun solmujen lukumäärä. Täten solmun x järjestysluku binäärisen etsintäpuun määritelmän nojalla on r = x.left.size + 1. Mikäli i = r, silloin i:s järjestysluku läytyy solmusta x ja se palautetaan rivillä 4. Mikäli i < r, on haettavan järjestysluvun läydyttävä vasemmasta alipuusta, jota siirrytään tarkastelemaan rivillä 6. Muussa tapauksessa siirrytään oikeaan alipuuhun, jonka juurena on x.right. Tälläin solmun x määrittämän alipuun i:nneksi pienin alkio on solmun x.right määrittämän alipuun (i r):nneksi pienin alkio. Esimerkki. Tarkastellaan kutsua OS SELECT(T.root, 9), missä T on kuvan 8.2.3 puu. Merkitään x0 = T.root. Koska x0.left.size + 1 = 4 < 9, läytyy 9:nneksi pienin alkio x0:n oikeasta alipuusta. Olkoon x1 = x0.right (eli x1.key = 31, x1.size = 7). Haettu alkio on solmun x1 määrittämän alipuun 9 4 = 5:nneksi pienin alkio, koska x0:n määrittämässä puussa on yhteensä 4 kaikkia x1:n määrittämän alipuun alkioita pienempi alkioita. Kutsuna on tälläin OS SELECT(x0.right, 5). Nyt x1.left.size + 1 = 4 < 5, jolloin 5:nneksi pienin alkio läytyy jälleen oikeasta alipuusta kutsulla OS SELECT(x1.right, 5 4). Ks. kuva 8.2.3. Olkoon x2 = x1.right. Tälläin x2.left.size + 1 = 2 > 1, jolloin haettu alkio läytyy vasemmasta alipuusta kutsulla OS SELECT(x2.left, 1). Olkoon x3 = x2.left. Koska x3:lla ei ole vasenta lapsisolmua, on x3.left.size = 0 ja rivin 3 ehto toteutuu. Siis x3 on haettu solmu. Ks. kuva 8.2.4. Tarkastellaan seuraavaksi algoritmin OS SELECT aikakompleksisuutta. Koska pahimmassa tapauksessa jokaisella rekursiokutsulla siirrytään puussa syvemmälle kunnes ollaan lehtisolmussa, riippuu pahin tapaus puun korkeudesta. Koska kyseessä on puna-musta puu, on sen korkeus O(lg n), missä n on solmujen lukumäärä. Täten algoritmin OS SELECT aikakompleksisuus n:n alkion dynaamiselle joukolle on O(lg n). 8.2.2 Alkion järjestysluvun määrittäminen Olkoon T järjestystunnuspuu ja x puun T solmu. Seuraavaksi määritellään algoritmi OS RANK(T, x), jolla voidaan määrittää solmun x järjestysluku.

Kuva 8.2.3: Esimerkki: OS SELECT Kuva 8.2.4: Esimerkki: OS SELECT OS_RANK (T, x) 1. r = x. left. size + 1 2. y = x 3. while y!= T. root 4. if y == y. parent. right 5. r = r + y. parent. left. size + 1 6. y = y. parent 7. return r Algoritmi toimii siten, että aluksi lasketaan solmun x järjestysluku puussa, jonka juuri x on. Tämän jälkeen siirrytään kohti puun T juurta. Mikäli tarkasteltu solmu y on vasen lapsisolmu, järjestyslukua ei tarvitse päivittää, sillä tällä askeleella ylöspäin ei läytynyt uusia solmuja, jotka ovat järjestyksessä edellä. Jos

solmu y on oikea lapsi, järjestyslukua kasvatetaan sisarsolmun (y.parent.lef t) määrittämän alipuun koolla (+ y:n vanhemmalla). Seuraavan silmukkainvariantin avulla voidaan osoittaa algoritmin oikeellisuus: Silmukkainvariantti: Jokaisen while-silmukan alussa muuttuja r sisältää annetun solmun x järjestysluvun siinä alipuussa, jonka juuri on solmu y. Tämä silmukkainvariantti on voimassa algoritmille OS RANK seuraavan perustelun nojalla: Alustus: Suorituksen alussa asetetaan r solmun x järjestysluvuksi siinä puussa, jonka juuri on x. Koska rivillä 3 asetetaan y = x, niin silmukkainvariantti on voimassa. Ylläpito: Tarkastellaan while-silmukan yhtä iterointikertaa. Lopussa (rivi 7) asetetaan y = y.parent. On osoitettava, että mikäli alussa (rivi 5) r on solmun x järjestysluku alipuussa, jonka juuri on y, niin lopussa (rivi 7) r on solmun x järjestysluku alipuussa, jonka juuri on y.parent. Tarkastellaan puuta, jonka juuri on y.parent. Aiemmin on jo laskettu solmun y määrittämän alipuun kattava järjestysluku. Täytyy siis huolehtia että uuden juurisolmun (y.parent) molemmat alipuut tulevat käsiteltyä. Jos y on uuden juuren vasen lapsi, niin uuden juuren oikea alipuu voidaan jättää käsittelemättä, koska y.parent eikä yksikään solmun y.parent oikeassa alipuussa oleva solmu voi sisältää y:tä pienempää arvoa. Muussa tapauksessa y on uuden juuren oikea lapsi (y == y.parent.right). Muuttujaa r päivitetään uuden juuren vasemman alipuun koon mukaisesti (r = r+y.parent.left.size+1), koska kaikki nämä arvot ja y.parent ovat solmun y arvoa pienempiä. Tämä varmistaa, että silmukan lopussa (rivi 7) r on solmun x järjestysluku alipuussa, jonka juuri on y.parent. Lopetus: While-silmukasta poistutaan kun järjestystunnuspuun juuri on sijoitettu muuttujaan y. Tälläin r sisältää solmun x järjestysluvun koko järjestystunnuspuun osalta. Jokainen while-silmukan iteraatio suoritetaan ajassa O(1). Lisäksi puussa siirrytään joka iteraatiolla taso yläspäin kohti juurta. Täten algoritmin OS RANK pahimman tapauksen suoritusaika on O(lg n), missä n on solmujen lukumäärä. Esimerkki. Tarkastellaan esimerkkinä kutsua OS RANK(T, x), missä T ja x ovat esitettynä kuvassa 8.2.5. Koska x:llä ei ole lapsisolmuja, asetetaan r = 1.

Merkitään y0 = x. Koska y0 ei ole puun juuri, siirrytään while-silmukkaan. Tässä tilanteessa y0 ei ole vanhempansa oikea lapsi, joten r:n arvoa ei tarvitse muuttaa, vaan siirrytään puussa yläspäin solmuun y1 = y0.parent. Vieläkään ei olla juuressa T.root, jolloin while-silmukka jatkuu. Tälläkin kerralla y1 on vanhempansa vasen lapsi, joten r jätetään ennalleen ja siirrytään solmuun y2 = y1.parent. Nyt y2 on vanhempansa oikea lapsi, jolloin päivitetään r = r + y2.parent.left.size + 1 = 5 ja siirrytään solmuun y3 = y2.parent. Nyt on saavutettu juuri, jolloin while-silmukkaa ei jatketa, vaan palautetaan solmun x järjestysluku 5. Kuva 8.2.5: Esimerkki : OS RANK Sekä OS SELECT että OS RANK suoriutuvat tehokkaasti, koska solmut sisältävät lisämuuttujan size. Tämä kuitenkin vaatii, että x.size pysyy oikeellisena jokaiselle puun solmulle x, mikäli puuta muokataan. Tämä vaatii muutoksia puna-mustan puun operaatioihin. Kuten aiemmin on nähty, solmun lisäys puuhun koostuu kahdesta vaiheesta. Ensimmäisessä vaiheessa kuljetaan juuresta alaspäin ja etsitään solmun paikka puussa. Toisessa vaiheessa palataan juurta kohti vaihtaen solmujen väritystä ja tekemällä rotaatioita, jotta puna-mustan puun ehdot pysyvät voimassa. Muuttujan size päivitys voidaan näissä kahdessa vaiheessa taata seuraavasti. Ensimmäisessä vaiheessa kasvatetaan jokaisen solmun x muuttujaa x.size yhdellä, mikäli kyseinen solmu oli polulla kun lisättävän solmun z paikkaa etsittiin. Lisättävälle solmulle asetetaan z.size = 1. Koska polulla on korkeintaan O(lg n) solmua, kasvattaa tämä aikakompleksisuutta O(lg n) verran. Toisessa vaiheessa puun rakenteeseen vaikuttavat rotaatiot, joita voi olla maksimissaan kaksi kappaletta. Lisäksi rotaatiot voivat aiheuttaa ainoastaan

kahden solmun size-muuttujan muuttumisen virheelliseksi. Jotta tämä voidaan korjata, on algoritmin LEFT ROTATE loppuun lisättävä rivit: y. size = x. size x. size = x. left. size + x. right. size + 1 Symmetrinen lisäys on luonnollisesti tehtävä algoritmiin RIGHT ROTATE. Koska solmun lisäyksessä on tehtävä korkeintaan kaksi rotaatiota, sizemuuttujan päivittämiseen käytettävä lisäaika on O(1). Tälläin solmun lisäys järjestystunnuspuuhun on aikakompleksisuudeltaan O(lg n), mikä on asymptoottisesti samaa luokkaa normaalin puna-mustan puun kanssa. Solmun poistaminen puna-mustasta puusta tapahtuu myös kahdessa vaiheessa. Ensimmäisessä vaiheessa operoidaan alla olevaan etsintäpuuhun. Tälläin poistettu solmu mahdollisesti aiheuttaa toisten solmujen kulkeutumisen lähemmäs juurta. Jotta solmujen size-muuttujat pysyvät oikeina, on kuljettava poistetun solmun paikalta juureen ja vähennettävä polulla olevien solmujen sizemuuttujaa yhdellä. Koska polun pituus on O(lg n), tulee poisto-operaatioon lisätyätä ensimmäisessä vaiheessa O(lg n) verran. Toisessa vaiheessa puun rakenne korjataan korkeintaan kolmella rotaatiolla. Kuten lisäyksessä, rotaatioiden aiheuttama lisäaika on O(1), jolloin poiston kokonaiskompleksisuudeksi saadaan n:n solmun järjestystunnuspuulle O(lg n). 8.2.3 Tietorakenteiden täydentämisen formalismi Algoritmien suunnittelussa joudutaan usein täydentämään perustietorakenteita juuri kuvatulla tavalla. Kuten nähtiin, tietorakenteiden täydentäminen koostuu seuraavasta neljästä pääkohdasta: 1. Alla olevan tietorakenteen valinta. 2. Täydentämisen kautta lisättävän informaation tarkka määrittely. 3. Uuden informaation oikean käsittelyn verifiointi (tietorakenteen perusoperaatioille). 4. Uusien operaatioiden kehittäminen. Tätä listausta ei yleensä suoriteta numerojärjestyksessä itse täydennysprosessin aikana, mutta se toimii korkean tason jaotteluna täydentämiseen liittyvässä työssä, ja etenkin tehdyn työn dokumentoinnissa. Nämä kohdat käytiin läpi järjestystunnuspuun suunnittelussa seuraavasti:

1. Perusrakenne on puna-musta puu. 2. Täydentävä informaatio on size-muuttuja jokaisessa solmussa. 3. size-muuttujan ylläpito selvitettiin erikseen. 4. Uusina operaatioina esiteltiin OS SELECT ja OS RANK. Tarkastellaan vielä puna-mustan puun täydentämistä. Puna-mustille puille voidaan osoittaa hyödyllinen tulos, joka suoraviivaistaa kohtaa 3: Olkoon f jokin tietue, joka täydentää puna-mustaa puuta T. Olkoon T :ssä n solmua. Oletetaan, että jokaista solmua x kohti f riippuu ainoastaan solmuista x, x.lef t ja x.right (mukaanlukien x.lef t.f ja x.right.f). Tällöin tietuetta f voidaan ylläpitää jokaisessa puun T solmussa x puuta koskevien lisäys- ja poisto-operaatioiden aikana ilman, että suorituskykyraja O(lg n) rikkoutuu. Todistus tälle tulokselle läytyy viitteestä [1]. 8.2.4 Intervallipuut Seuraavassa täydennetään puna-mustia puita tukemaan tietorakenneoperaatioita, jotka käsittelevät dynaamista joukkoa suljettuja intervalleja [t 1, t 2 ] = {t R t 1 t t 2 }. Tällainen tietorakenne on hyödyllinen, kun esimerkiksi mallinnetaan joukkoa tapahtumia, joilla on äärellinen ajallinen kesto. Intervalli [t 1, t 2 ] voidaan esittää alkiona i, joka sisältää muuttujat i.low = t 1 (alkupiste) sekä i.high = t 2 (loppupiste). Intervallit i ja i toteuttavat nk. intervallitrikotomia -ehdon (ks. kuva 8.2.6) eli täsmälleen yksi seuraavista ehdoista on tosi: (a) i i, eli intervallit limittyvät, (b) i on intervallin i vasemmalla puolella (i.high < i.low), (c) i on intervallin i oikealla puolella (i.high < i.low). Intervallipuu T on puna-musta puu, joka ylläpitää dynaamista joukkoa jossa jokainen alkio x sisältää intervallin x.int. Tuettuna ovat seuraavat operaatiot: INTERVAL INSERT(T, x): Lisää intervallin sisältävän alkion x intervallipuuhun T. INTERVAL DELETE(T, x): Poistaa alkion x intervallipuusta T.

Kuva 8.2.6: Intervallien trikotomia INTERVAL SEARCH(T, i): Palauttaa osoittimen alkioon x intervallipuussa T, missä x.int limittyy intervallin i kanssa, tai None muutoin. Seuraavaksi tarkastellaan, miten intervallipuun muodostus etenee edellä esitettyjen neljän täydennysperiaatteen mukaisesti. 1. Alla olevan tietorakenteen valinta: Valitaan puna-musta puu, jossa jokainen alkio x sisältää intervallin x.int ja alkion avain on x.int.low. Toisin sanottuna, intervallipuu listaa alkiot vastaavien intervallien alarajojen mukaiseen järjestykseen. 2. Täydentämisen kautta lisättävän informaation tarkka määrittely: Itse intervallien lisäksi jokainen alkio x sisältää myös arvon x.max, joka ilmaisee alkion x määrittämän alipuun sisältämien intervallien loppupisteiden maksimiarvon. 3. Uuden informaation oikean käsittelyn verifiointi: Osoitetaan, että intervallien lisäys- ja poisto-operaatiot n alkiota sisältävälle intervallipuulle voidaan edelleen suorittaa ajassa O(lg n). Muuttuja x.max voidaan määrittää intervallista x.int ja solmun x lapsisolmujen max-muuttujista: x.max = max{x.int.high, x.lef t.max, x.right.max}. Koska x.max riippuu ainoastaan solmuista x, x.lef t ja x.right, niin kappaleessa 8.2.3 esitetyn tuloksen nojalla lisäys ja poisto voidaan suorittaa ajassa O(lg n). 4. Uusien operaatioiden kehittäminen: Täydennetylle tietorakenteelle esitellään yksi uusi operaatio: INTERVAL SEARCH:

INTERVAL_SEARCH (T, i) 1. x = T. root 2. while x!= None and 3. " i ei limitt ä ydy intervallin x. int kanssa " 4. if x. left!= None and x. left. max >= i. low 5. x = x. left 6. else 7. x = x. right 8. return x Tarkastellaan tämän algoritmin kompleksisuutta. Intervallin haku aloitetaan juuresta T.root ja se etenee askel kerrallaan puussa alaspäin. Haku loppuu mikäli läydetään limittäytyvä intervalli x.int tai saavutetaan solmu, joka on N one. While-silmukan sisältö suoritetaan ajassa O(1). Koska n alkiota sisältävän puna-mustan puun korkeus on O(lg n), niin algoritmin aikakompleksisuus on tällöin O(lg n). Vielä on osoitettava algoritmin INTERVAL SEARCH oikeellisuus. Sitä ennen tarkastellaan esimerkkiä ko. algoritmin toiminnasta. Esimerkki. Tarkastellaan kutsua INTERVAL SEARCH(T, i), missä i = [5, 6] ja T on esitetty kuvassa 8.2.7. Algoritmi aloittaa haun juuresta Kuva 8.2.7: Esimerkki: INTERVAL SEARCH x0 = T.root. Koska intervalli i = [5, 6] ei limittäydy intervallin x0.int = [16, 21] kanssa, niin siirrytään suorittamaan while-silmukan sisältää. Koska x0.left on olemassa ja x.left.max = 23 > i.low = 5, niin siirrytään vasempaan lapsisolmuun x1 = x0.lef t. Vieläkään i ei limittäydy intervallin x1.int = [8, 9] kanssa, jolloin while-silmukka jatkuu. Nyt x1.lef t.max =

8 > i.low = 5, ja siirrytään jälleen vasempaan lapsisolmuun x2 = x1.left. Nyt i x2.int = [5, 6] [5, 8] = [5, 6] ja intervallit limittyvät. Siis x2 on haettu solmu. Osoitetaan nyt algoritmin INTERVAL SEARCH oikeellisuus. Esimerkin perusteella voidaan päätellä seuraava silmukkainvariantti: Silmukkainvariantti: Jos intervallipuu T sisältää intervallin joka limittyy annetun intervallin i kanssa, se löytyy solmun x määrittämästä alipuusta. Tämän perusteella saadaan: Alustus: Ensimmäisellä rivillä asetetaan x = T.root, jolloin invariantti on voimassa. Ylläpito: Jokaisella while-silmukan iteraatiolla siirrytään joko vasempaan tai oikeaan alipuuhun. Mikäli siirrytään oikeaan alipuuhun, niin joko a) x.left == None. Tällöin sijoitus x = x.right säilyttää silmukkainvariantin. b) x.left None, mutta x.left.max < i.low. Tällöin intervallitrikotomian perusteella mikään vasemman alipuun intervalli ei limity intervallin i kanssa, ja sijoitus x = x.right säilyttää silmukkainvariantin. Mikäli siirrytään vasempaan alipuuhun, niin x.lef t.max i.low. Silmukkainvariantti pätee jos ja vain jos seuraava ehto on voimassa: Jos vasemmasta alipuusta ei löydy annetun intervallin kanssa limittyvää intervallia, niin sitä ei löydy koko puusta. Oletetaan, että vasemmasta alipuusta ei läydy limittyvää intervallia ja osoitetaan että tämä väite pitää. Muuttujan max määritelmän perusteella täytyy olla jokin intervalli i jolle i.high = x.left.max i.low. Koska oletuksen nojalla i ja i eivät limity, niin on oltava i.high < i.low. Koska lisäksi intervallipuu on avainnettu intervallien alkupisteiden perusteella, niin etsintäpuun ehdon nojalla jokaiselle solmun x oikeassa alipuussa olevalle intervallille i pätee i.high < i.low i.low, jolloin intervallitrikotomian nojalla i ja i eivät limity. Siis puussa ei ole i:n kanssa limittyvää intervallia ja väite on tosi. Täten sijoitus x = x.lef t säilyttää silmukkainvariantin.

Lopetus: Mikäli while-silmukasta poistutaan kun x = N one, niin solmun x määrittämät alipuut ovat tyhjiä eivätkä siis voi sisältää intervallin i kanssa limittyvää intervallia. Lisäksi tällöin while-silmukan limittymisehto ei täyttynyt kertaakaan, joten intervallipuu T ei sisällä intervallin i kanssa limittyvää intervallia. Tämän nojalla INTERVAL SEARCH toimii oikein.

Kirjallisuutta [1] Cormen, T.H., Leiserson, C.E., Rivest, R.L., Stein, C. Introduction to Algorithms, 2nd edition, The MIT Press 2001. [2] Stallings, W. Cryptography and Network Security, Fourth Edition, Prentice Hall 2006. [3] Weiss, M. A. Data structures and algorithm analysis in Java, Addison Wesley 1999. 13