1 Tietorakenteet (kevät 08) 1. kurssikoe, ratkaisuja Tehtävän 1 korjasi Mikko Heimonen, tehtävän 2 Jaakko Sorri ja tehtävän Tomi Jylhä-Ollila. 1. (a) Tehdään linkitetty lista kaikista sukunimistä. Kuhunkin listan solmuun liittyy paitsi sukunimi, myös osoitin kaikki siihen liittyvät etunimet sisältävään listaan. Allaolevassa esimerkissä hakemisto sisältää nimet Maija Niemi, Ville Niemi, Tauno Niemi, Tuula Järvi, Matti Lahti ja Maija Lahti. Listojen toteutukseen on tässä valittu kahteen suuntaan linkitetty rengaslista. head Niemi Maija Ville Tauno sukunimi Järvi Tuula alku loppu taakse eteen sukunimisolmu Lahti Matti Maija etunimi eteen taakse etunimisolmu (b) TULOSTA-SUKUNIMET if head NIL then p head repeat print sukunimi[p] p eteen[p] until p = head TULOSTA-ETUNIMET(suku) if head NIL then p head if sukunimi[p] suku then repeat p eteen[p] until sukunimi[p] = suku or p = head if sukunimi[p] = suku then q alku[p] while q p do print etunimi[q] q eteen[q]
(c) INSERT(etu, suku) if head = NIL then p new sukunimisolmu sukunimi[p] suku eteen[p] taakse[p] alku[p] loppu[p] p else p head if sukunimi[p] suku then repeat p eteen[p] until sukunimi[p] = suku or p = head if sukunimi[p] suku then p new sukunimisolmu sukunimi[p] suku alku[p] loppu[p] p eteen[p] head taakse[p] taakse[head] taakse[head] p eteen[taakse[p]] p head p q new etunimisolmu etunimi[q] etu eteen[q] eteen[p] taakse[q] p eteen[p] q taakse[eteen[q]] q Arvostelusta: Täydet pisteet oli mahdollista saada paitsi listoja sisältävällä listalla myös esimerkiksi listoja sisältävälllä hakupuulla (tai hakupuita sisältävällä hakupuulla), kunhan funktiot toimivat kuten määriteltyä ja hakuoperaatioiden aikavaativuudet eivät ylittyneet. Joitakin yleisiä virheitä: Jos ratkaisu perustui kokonaan taulukoihin, joiden oletettiin olevan äärettömän kokoisia, oli maksimi 4 pistettä. Jos INSERT kasvatti taulukkoa tarpeen mukaan, sai täydet pisteet, mutta ainoa tällaista ratkaisua ehdottanut paperi sisälsi muita ongelmia. Jos ratkaisuna oli taulukkoja sisältävä lista (jonka taulukot vuotivat yli), sai yleensä 6 pistettä. Jos ratkaisu oli yksi linkitetty lista, jonka solmut sisälsivät etunimi, sukunimi -pareja, oli maksimi 4 pistettä, koska tällöin hakuoperaatiot toimivat ajassa O(lisättyjen henkilöiden määrä), joka on eri kuin O(n) tai O(n + m). Lisäksi jos listaa ei pidetty järjestyksessä, tulostuivat sukunimet yleensä useampaan kertaan, mistä menetti vielä yhden pisteen. Jos ratkaisuissa käytti jotain listaoperaatiota (kuten SEARCH) eikä esittänyt itse sen toteutusyksityiskohtia, menetti yhden pisteen. Useammasta saman operaation käytöstä ei rangaistu erikseen. Lisäksi koodin epäloogisuudet tai puutteet toiminnallisuudessa verottivat pisteitä. 2. (a) AVL-puussa jokaisen solmun vasemman ja oikean alipuun korkeuksien ero saa olla vain 1, 0 tai +1. AVL-tasapainoehdosta seuraa, että n-solmuisen puun korkeus on O(log n). Tästä edelleen seuraa, että joukko-operaatiot n-alkioisella joukolla toimivat pahimmassakin tapauksessa ajassa O(log n), kun tasapainottamattomalla binääripuulla pahimman tapauksen aikavaativuudet ovat O(n). Arvostelusta: Yksi piste tasapainoehdosta, toinen AVL-puun edusta tasapainottamattomaan binääripuuhun nähden. Kohta oli osattu pääsääntöisesti hyvin ja pistemenetyksiä tuli etupäässä ylimal-
kaisuuksista. Kahta pistettä ei saanut vain toteamalla, että AVL-puut ovat nopeampia, jos ei vihjaissut mitään siitä, missä ne ovat nopeampia tai miksi. (b) Lisätään alkio, jolloin solmu joutuu epätasapainoon (vasemman alipuun korkeus 2, oikean 0): 4 1 4 2 1 7 7 Kierretään oikealle solmussa, jolloin puu päätyy tasapainoon: 4 1 4 2 1 7 7
Avaimen 8 lisääminen ei riko puun tasapainoa, joten kiertoja ei tarvita: 4 1 4 2 1 7 8 7 Avaimen 27 lisääminen saattaa solmun epätasapainoon (vasemman alipuun korkeus, oikean 1): 4 1 4 2 1 7 8 7 27
Koska lisäys on tehty solmun vasemman lapsen oikeaan alipuuhun, tarvitaan vasen-oikeakaksoiskierto: 4 1 4 2 1 7 8 27 7 Arvostelusta: Kohdasta oli mahdollista saada täysin oikealla vastauksella ja riittävillä selityksillä tai välivaiheiden piirtämisellä pistettä. Kustakin virheestä ja olennaisten välivaiheiden puuttumisesta rokotettiin yhdellä pisteellä. Kohta oli osattu varsin hyvin. Harmittavan monelta oli kuitenkin jäänyt huomaamatta, että avaimen lisääminen vie solmun epätasapainoon, vaikka tasapainoehto olikin a-kohdassa osattu. Myös avaimen 27 lisäämisen aiheuttama kaksoiskierto aiheutti monelle päänvaivaa. Täydet pisteet sai vain näyttämällä, miten kaksoiskierto tehdään oikeaoppisesti. Vastauksista, joissa oli päädytty oikeaan lopputilanteeseen ilman selityksiä tai välivaiheita vähennettiin yksi piste, samaten niistä, joissa lopputilanne oli laillinen AVL-puu, mutta matkan varrella tehtiin laittomia kiertoja tai tarpeettoman monta laillista kiertoa. Jos lopputilanne ei täyttänyt AVL-puun tasapainoehtoa, vähennettiin kaksi pistettä. (c) Koska solmulla on kaksi lasta, normaali binääripuun poisto siirtää avaimen sisältäneeseen solmuun seuraaja-avaimen. Avaimen sisältänyt solmu poistetaan puusta. Puu on edelleen tasapainossa: 4 1 4 2 1 7 7
Avain 7 on lehdessä, joka voidaan suoraan poistaa. Nyt solmu joutuu epätasapainoon (vasemman alipuun korkeus 0, oikean 2): 4 1 4 2 1 7 Solmu tasapainottuu tekemällä siinä oikea-vasen-kaksoiskierto: 4 1 4 2 1 7
Nyt kuitenkin solmu joutui epätasapainoon (vasemman alipuun korkeus 4, oikean 2). Koska korkein alipuu on vasemman lapsen oikea alipuu, tehdään vasen-oikea-kaksoiskierto: 4 1 4 2 1 7 Puu on nyt tasapainossa. Arvostelusta: Pisteytysperusteet samat kuin edellä, eli max. pistettä, virheistä rokotettiin yksi piste. Välivaiheiden näyttämistä pyydettiin tehtävänannossa, joten täysiä pisteitä ei tässäkään kohdassa saanut pelkillä oikeilla lopputilanteilla. Kohta näytti olleen tehtävän vaikein. Avainta poistettaessa seuraajan kaivaminen puusta aiheutti hankaluuksia, mikä johti usein eriskummallisten operaatioiden sarjaan. Näistä vähennettiin pääsääntöisesti vain yksi piste. Lehden 7 poistaminen oli osattu paremmin ja myös siitä aiheutuva solmun epätasapaino oli huomattu mukavasti. Tosin tasapainotuksen vaatima kaksoiskierto aiheutti joillekin päänvaivaa, ja muutamissa vastauksissa lopputilanteeksi jäi laiton binääripuu (yleensä avain oli avaimen oikea lapsi), mistä rokotettiin armotta. Yleisempi virhe oli kuitenkin se, että solmun tasapainotuksen aiheuttama epätasapaino solmussa jäi huomaamatta. Täydet pisteet sai vain huomaamalla kaikki epätasapainot ja näyttämällä oikeat kierrot puun tasapainottamiseksi. Välivaiheiden puuttuminen tulkittiin virheeksi.. (a) Väite: Epätyhjässä täydessä binääripuussa lehtien lukumäärä = sisäsolmujen lukumäärä + 1. Todistus: Tehdään induktio puun korkeuden suhteen. Koska väite koskee epätyhjiä puita, korkeuden 1 osalta ei ole mitään todistettavaa. Jos T on täysi binääripuu, jonka korkeus on nolla, siinä on yksi lehti ja nolla sisäsolmua. Väite siis pätee. Tehdään nyt induktio-oletus, että väite pätee kaikilla puille, joiden korkeus on korkeintaan h 0. Olkoon T täysi binääripuu, jonka korkeus on h+1. Siis T :n vasemman ja oikean alipuun korkeudet ovat kumpikin korkeintaan h (ja ainakin toinen on tasan h, mutta sitä ei tässä tarvita). Oletuksen mukaan T on täysi eli jokaiselle sen solmulla on 0 tai 2 lasta; tämä pätee erityisesti vasemman ja oikean alipuun solmuille, joten oikea ja vasen alipuu ovat täysiä. Lisäksi myös juurella on kaksi lasta (koska korkeus ei ole nolla), eli T :n vasen ja oikea alipuu ovat epätyhjiä. Vasen ja oikea alipuu ovat siis epätyhjiä täysiä binääripuita, joiden korkeus on korkeintaan h, ja niihin voidaan soveltaa induktio-oletusta. Olkoon vasemman alipuun sisäsolmujen lukumäärä s L ja lehtien l L ; oikeassa alipuussa vastaavasti s R ja l R. Induktio-oletuksen nojalla l L = s L + 1 ja l R = s R + 1. Olkoon s T koko puun T sisäsolmujen lukumäärä ja l T lehtien lukumäärä. Puun T lehtiä ovat vasemman ja oikean alipuun lehdet. Puun T sisäsolmuja ovat vasemman ja oikean alipuun sisäsolmut sekä lisäksi juuri. Saadaan l T = l L + l R = (s L + 1) + (s R + 1) (induktio-oletus) = (s L + r L + 1) + 1 = s T + 1
eli väite pätee myös puulle T. Arvostelusta: Vastaukset on arvosteltu seuraavien tekijöiden mukaisesti. Perustapaus on käsitelty oikein (2 p). Tässä riittää esitellä sellainen tapaus, jonka voi täydentää sopivalla induktioaskeleella siten, että väite tulee osoitetuksi kaikille täysille binääripuille. Yksinkertaisin tapa on esitellä yksisolmuinen puu. Induktioaskel on tehty oikein (6 p): Induktio-oletuksessa käytetään täysiä puita, joihin oletusta voidaan soveltaa (2 p). Induktioaskelta toistuvasti soveltamalla voidaan muodostaa kaikki täydet binääripuut perustapauksesta lähtien (2 p). Uuden puun lehtien ja sisäsolmujen määrä on laskettu oikein (2 p). (Pisteitä olisi vähennetty, mikäli itsessään validi perustapaus ja induktioaskel eivät olisi sopineet yhteen toistensa kanssa, mutta tällaisia vastauksia ei ollut.) Moni vastaajista oli esittänyt todistuksen täydellisille binääripuille, jotka ovat erikoistapaus täysistä puista. Tästä annettiin esitystarkkuudesta riippuen 2 tai pistettä. Yleinen puute muuten oikeissa ratkaisuissa oli se, että induktioaskeleessa ei käytetty nimenomaan täysiä binääripuita eikä perusteltu, miksi niistä muodostettu uusi puu on täysi. (b) Tehtävänä oli esittää yksityiskohtainen pseudokoodi algoritmille POISTA-PIENEMMÄT(T,k), joka poistaa puusta T kaikki avainta k pienemmät avaimet. POISTA-PIENEMMÄT(T, k) root[t] PIENEMMÄT-POISTETTU(root[T], k) PIENEMMÄT-POISTETTU(x, k) if x = NIL then return NIL elseif key[x] k then Poistot tehdään vasemmassa alipuussa left[x] PIENEMMÄT-POISTETTU(left[x], k) return x else Juuri ja vasen alipuu poistetaan kokonaan return PIENEMMÄT-POISTETTU(right[x], k) Arvostelusta: Vastaukset on arvosteltu seuraavien tekijöiden mukaisesti. Algoritmi toimii oikein (8 p). Pienempi avain löydetään oikein (2 p). Pienemmän avaimen löytyessä se ja sen vasen alipuu poistetaan oikein ja jatketaan tarkastelua oikeassa alipuussa (s.e. oikeaan alipuuhun säilytetään viite oikein) (4 p). Puussa eteneminen päättyy oikein (2 p). Jos algoritmin aikavaativuus ei ollut O(h), vähennettiin 1 p. Isäviitteiden käyttö on sallittu, kunhan nämä viitteet päivitetään poistojen yhteydessä oikein. Tyypillinen virhe oli poistaa vasen alipuu kokonaan sellaiselta solmulta x, jolle key[x] = k. Täältä voi kuitenkin edelleen löytyä solmuja, joilla on yhtä suuri avain. Monissa vastauksissa viitteitä lapsisolmuihin (ja isäsolmuihin, mikäli ne olivat käytössä) ei ollut päivitetty oikein, vaan poistettavan solmun oikea alipuu hukattiin. Myös DELETE-operaation käyttö oli suosittua, mutta tämä johti algoritmin hidastumiseen. Lisäksi poistetun solmun korvaajaa ei ollut aina muistettu käsitellä.