Kierros 1: Algoritmianalyysin ja tietorakenteiden perusteita

Samankaltaiset tiedostot
CS-A1140 Tietorakenteet ja algoritmit

A TIETORAKENTEET JA ALGORITMIT

Algoritmit 1. Luento 2 Ke Timo Männikkö

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Tietorakenteet ja algoritmit - syksy

4 Tehokkuus ja algoritmien suunnittelu

Algoritmit 1. Luento 3 Ti Timo Männikkö

Algoritmit 1. Luento 4 Ke Timo Männikkö

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

811312A Tietorakenteet ja algoritmit II Perustietorakenteet

Tieto- ja tallennusrakenteet

Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

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

Algoritmit 2. Luento 14 Ke Timo Männikkö

Tietorakenteet, laskuharjoitus 3, ratkaisuja

Algoritmit 2. Luento 2 To Timo Männikkö

Algoritmit 2. Luento 8 To Timo Männikkö

Algoritmit 2. Luento 2 Ke Timo Männikkö

1.4 Funktioiden kertaluokat

Tietorakenteet ja algoritmit

18. Abstraktit tietotyypit 18.1

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

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

Algoritmit 2. Luento 7 Ti Timo Männikkö

ALGORITMIT 1 DEMOVASTAUKSET KEVÄT 2012

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Algoritmit 1. Luento 5 Ti Timo Männikkö

Algoritmit 1. Luento 14 Ke Timo Männikkö

Ohjelmoinnin perusteet Y Python

Tietorakenteet ja algoritmit

TIETORAKENTEET JA ALGORITMIT

811312A Tietorakenteet ja algoritmit, , Harjoitus 3, Ratkaisu

2. Perustietorakenteet

Algoritmit 2. Luento 1 Ti Timo Männikkö

Algoritmit 1. Luento 12 Ti Timo Männikkö

Algoritmit 1. Luento 10 Ke Timo Männikkö

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

List-luokan soveltamista. Listaan lisääminen Listan läpikäynti Listasta etsiminen Listan sisällön muuttaminen Listasta poistaminen Listan kopioiminen

Algoritmit 1. Luento 11 Ti Timo Männikkö

Algoritmit 1. Demot Timo Männikkö

Abstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005

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

Algoritmit 1. Luento 12 Ke Timo Männikkö

Ohjelmoinnin perusteet Y Python

Kierros 2: Järjestämisalgoritmeja

Kierros 6: Dynaaminen ohjelmointi ja ahneet algoritmit

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.

58131 Tietorakenteet ja algoritmit (syksy 2015)

Ohjelmoinnin peruskurssi Y1

Kierros 4: Binäärihakupuut

A TIETORAKENTEET JA ALGORITMIT

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

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

Diskreetin matematiikan perusteet Laskuharjoitus 2 / vko 9

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

Algoritmianalyysin perusteet

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

5 Kertaluokkamerkinnät

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

Johdatus diskreettiin matematiikkaan Harjoitus 5, Ratkaise rekursioyhtälö

Algoritmit 1. Demot Timo Männikkö

Algoritmit 1. Demot Timo Männikkö

Algoritmit 2. Demot Timo Männikkö

Tietorakenteet (syksy 2013)

Harjoitustyö: virtuaalikone

STL:n uudistukset. Seppo Koivisto TTY Ohjelmistotekniikka

Luku 3. Listankäsittelyä. 3.1 Listat

MS-A0401 Diskreetin matematiikan perusteet

Tietorakenteet ja algoritmit syksy Laskuharjoitus 1

Ohjelmoinnin perusteet Y Python

Tietorakenteet, laskuharjoitus 1,

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Ohjelmoinnin perusteet Y Python

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

Tietorakenteet. JAVA-OHJELMOINTI Osa 5: Tietorakenteita. Sisällys. Merkkijonot (String) Luokka String. Metodeja (public)

Algoritmit 2. Luento 3 Ti Timo Männikkö

Algoritmit 1. Luento 10 Ke Timo Männikkö

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

ITKP102 Ohjelmointi 1 (6 op)

Algoritmit 1. Demot Timo Männikkö

Operaattoreiden ylikuormitus. Operaattoreiden kuormitus. Operaattoreiden kuormitus. Operaattoreista. Kuormituksesta

CS-A1140 Tietorakenteet ja algoritmit

Ohjelmoinnin perusteet Y Python

Kysymyksiä koko kurssista?

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin peruskurssien laaja oppimäärä

Ohjelmoinnin perusteet Y Python

811312A Tietorakenteet ja algoritmit, , Harjoitus 5, Ratkaisu

Osoitin ja viittaus C++:ssa

CS-A1140 Tietorakenteet ja algoritmit

Tietorakenteet ja algoritmit

Tutoriaaliläsnäoloista

Algoritmit 2. Luento 4 To Timo Männikkö

Algoritmit 2. Luento 3 Ti Timo Männikkö

Ohjelmoinnin peruskurssien laaja oppimäärä

CS-A1140 Tietorakenteet ja algoritmit

TIE Tietorakenteet ja algoritmit 25

Ohjelmoinnin peruskurssien laaja oppimäärä

Sisältö. 22. Taulukot. Yleistä. Yleistä

Concurrency - Rinnakkaisuus. Group: 9 Joni Laine Juho Vähätalo

Transkriptio:

Kierros 1: Algoritmianalyysin ja tietorakenteiden perusteita Tommi Junttila Aalto University School of Science Department of Computer Science CS-A1140 Data Structures and Algorithms Autumn 2017 Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 1 / 51

Materiaalia kirjassa Introduction to Algorithms, 3rd ed. (online via Aalto lib): Funktioiden kasvunopeuksista: Luku 3 Perustietorakenteita: Kappaleet 10.1 10.3 Dynaamiset taulukot: Kappale 17.4 Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 2 / 51

Algoritmien kuvauksista Erittäin abstraktilla tasolla algoritmi voidaan nähdä menetelmänä, joka muuntaa syötteen halutuksi tulokseksi input algorithm output Esimerkiksi järjestämisalgoritmi muuntaa mielivaltaisen taulukon (mahdollisesti toiseksi) taulukoksi, jossa samat alkiot ovat suuruusjärjestyksessä Monissa tapauksissa on parempi kuvata algoritmi joko luonnollisella kielellä tai pseudokoodina sen sijaan, että esittäisi jollakin tietyllä ohjelmointikielellä tehdyn toteutuksen Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 3 / 51

Tarkastellaan esimerkkinä CS-A1120 Ohjelmointi 2 -kurssilla esitettyä puolitushakualgoritmia Se saa syötteenään järjestetyn taulukon A ja alkion e ja selvittää, esiintyykö alkio taulukossa Voisimme kuvata algoritmin luonnollisella kielellä esimerkiksi seuraavasti: 1 Tarkastellaan aluksi koko taulukkoa 2 Jos taulukko on tyhjä, palauta false 3 Jos taulukon keskimmäinen alkio on e, palauta true 4 Muutoin jos keskimmäinen alkio oli suurempi kuin e, palaa kohtaan 2 mutta tarkastele vain osataulukkoa, joka sisältää nykyisen taulukon alkuosan alusta keskipisteeseen asti (muttei sisältäen keskipistettä) 5 Muutoin keskimmäinen alkio on pienempi kuin e ja palataan kohtaan 2 mutta tarkastellen osataulukkoa alkaen keskipisteestä seuraavasta alkiosta ja päättyen viimeiseen alkioon Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 4 / 51

Algoritmin kuvaus pseudokoodina: // Search for the element e in the sorted array A binary-search(a, e): i search(a, e, 1, A.length) if A[i] = e: return true else: return false // A helper function: search for the index of e in the sub-array A[lo,...,hi] search(a, e, lo, hi): if hi lo: return lo // sub-array of size 1 or less? mid lo + (hi lo)/2 if A[mid] = e: return mid else if A[mid] < e: return search(a, e, mid + 1, hi) else: return search(a, e, lo, mid 1) Huomioitavaa: monesti taulukon indeksointi alkaa indeksillä 1 pseudokoodissa ja matemaattisessa notaatiossa mutta indeksillä 0 monessa oikeassa ohjelmointikielissä Tämä tulee ottaa huomioon toteutettaessa kirjasta, wikipediasta jne löytyviä algoritmeja Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 5 / 51

Eräs toteutus Scalalla: def binarysearch [ T <% Ordered [ T ] ] ( s : IndexedSeq [ T ], v : T ) : Boolean = { def i n n e r ( s t a r t : I n t, end : I n t ) : I n t = { i f (! ( s t a r t < end ) ) s t a r t else { val mid = s t a r t + ( ( end s t a r t ) / 2) val cmp = v compare s ( mid ) i f (cmp == 0) mid / / v == s ( mid ) else i f (cmp < 0) i n n e r ( s t a r t, mid 1) / / v < s ( mid ) else i n n e r ( mid+1, end ) / / v > s ( mid ) } } i f ( s. length == 0) false else s ( i n n e r ( 0, s. length 1)) == v } Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 6 / 51

Algoritmianalyysin perusteita Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 7 / 51

Resurssityyppejä ja ominaisuuksia Tavoitteena on saada aikaan algoritmeja, jotka käyttävät annettuja resursseja tehokkaasti Tyypillisiä resursseja ja ominaisuuksia ovat ajoaika muistin käyttö rinnakkaistuvuus levyn ja verkon käyttö välimuistin käytön tehokkuus (esim. cache-oblivious algorithms)... Luonnollisesti joskus pitää tehdä kompromisseja: esimerkiksi algoritmi voi käyttää vähemmän muistia mutta olla ajoajaltaan hitaampi kuin toinen saman asian tekevä algoritmi Tällä kurssilla keskitytään pääsääntöisesti ajoaikaan Tarkastelemme algoritmin/ohjelman ajoaikaa funktiona f(n) suhteessa syötteen kokoon tai johonkin muuhun mielenkiintoiseen parametriin n Sekä n että f(n) oletetaan yleensä ei-negatiivisiksi ja n on yleensä kokonaisluku Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 8 / 51

O-notaatio Tarkastellaan funktioiden asymptoottista kasvua Tätä varten käytetään O-notaatiota ja sen variantteja Se on matemaattinen työkalu, joka abstrahoi pois Määritelmä pienet n:n arvot eli pienet syötteet, sekä vakiotekijät, jotka riippuvat esimerkiksi kellotaajuudesta tai muistiväylän leveydestä Merkitään f(n) = O(g(n)) jos on olemassa vakiot c,n 0 > 0 siten, että f(n) cg(n) kaikille n n 0. Tällöin f(n) kasvaa korkeintaan yhtä nopeasti kuin g(n) (kun tarkastelemme tarpeeksi suuria n:n arvoja) Muistutus: f(n) ja g(n) ovat ei-negatiivisia Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 9 / 51

10n + 1200 = O(n), 10n + 1200 = O(n 2 ) ja 10n + 1200 = O(2 0.1n ) n 2 + 5n + 1000 = O(n 2 ) ja n 2 + 5n + 1000 = O(2 0.1n ) mutta n 2 + 5n + 1000 O(n) 2 0.1n O(n) ja 2 0.1n O(n 2 ) Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 10 / 51

Tyypillisiä funktioluokkia: O(1) vakio O(log n) logaritminen O(n) lineaarinen O(n logn) kvasilineaarinen, n log n (O(logn!) = O(n logn)) O(n 2 ) neliöllinen O(n 3 ) kuutiollinen O(n k ) polynominen, k 0 O(c n ) eksponentiaalinen, c > 1 O(n!) kertoma Vakio-, logaritmiset, lineaariset, kvasilineaariset, neliölliset ja kuutiolliset funktiot ovat siis kaikki polynomisia funktioita Logaritmit ovat kannassa 2 jollei toisin mainita (mutta tällä ei yleisesti väliä koska log c n = log 2 n kaikille c > 1) log 2 c Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 11 / 51

Algoritmin/ohjelman pahimman tapauksen ajoaika on O(f(n)) jos g(n) = O(f(n)), missä funktio g(n) on pisin ajoaika mille tahansa syötteelle kokoa n Yleensä sanonta algoritmin ajoaika on O(f(n)) tarkoittaa pahimman tapauksen ajoaikaa Voidaan myös tarkastella parhaan tapauksen ajoaikaa, joka saadaan vastaavasti tarkastelemalla kaikkien n:n kokoisten syötteiden pienintä ajoaikaa keskimääräistä ajoaikaa, missä tarkastellaan n:n kokoisten syötteiden (joko kaikkien tai jostakin tyypillisestä joukosta valittujen) odotettua ajoaikaa Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 12 / 51

Esimerkki Tarkastellaan lineaarihakualgoritmia, joka tarkastaa kuuluuko alkio e järjestämättömään taulukkoon A käymällä sen alkiot läpi kunnes e löytyy tai kaikki taulukon n alkiota on käyty läpi. Olettaen, että alkion haku taulukosta ja alkioiden vertailu ovat vakioaikaisia operaatioita, pahimman tapauksen ajoaika on O(n) koska e voi olla taulukon viimeinen alkio tai puuttua taulukosta, jolloin kaikki n alkiota käydään läpi parhaan tapauksen ajoaika on O(1) koska e voi olla taulukon ensimmäinen alkio Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 13 / 51

Esimerkki: neliömatriisien (suoraviivainen) kertominen Analysoidaan kertolaskumetodin * asymptoottista ajoaikaa Ensin tulosmatriisin result alustus vie ajan O(n 2 ) koska sen n n alkiota varataan ja alustetaan arvolla 0 class Matrix ( val n : I n t ) { r e q u i r e ( n > 0, n must be p o s i t i v e ) val e n t r i e s = new Array [ Double ] ( n * n ) / * * Access elements by w r i t i n g m( i, j ) * / def apply ( row : I n t, column : I n t ) = { e n t r i e s ( row * n + column ) } / * * Set elements by w r i t i n g m( i, j ) = v * / def update ( row : I n t, c o l : I n t, val : Double ) { e n t r i e s ( row * n + c o l ) = val } / * * Returns the product of t h i s and t h a t * / def * ( t h a t : M atrix ) : M atrix = { r e q u i r e ( n == t h a t. n ) val r e s u l t = new Matrix ( n ) for ( row < 0 u n t i l n ; column < 0 u n t i l n ) { var v = 0.0 for ( i < 0 u n t i l n ) } } v += this ( row, i ) * t h a t ( i, column ) r e s u l t ( row, column ) = v } r e s u l t Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 14 / 51

Esimerkki: neliömatriisien (suoraviivainen) kertominen Uloin silmukka käy läpi muuttujalle row arvot 0,1,...,n 1 Keskimmäinen: col arvot 0,1,...,n 1 Sisin: i arvot 0,1,...,n 1 Sisimmässä silmukassa arvojen hakeminen ja kertolasku ovat vakioaikaisia toimintoja Metodin * ajoaika on O(n 2 + n 3 ) = O(n 3 ) class Matrix ( val n : I n t ) { r e q u i r e ( n > 0, n must be p o s i t i v e ) val e n t r i e s = new Array [ Double ] ( n * n ) / * * Access elements by w r i t i n g m( i, j ) * / def apply ( row : I n t, column : I n t ) = { e n t r i e s ( row * n + column ) } / * * Set elements by w r i t i n g m( i, j ) = v * / def update ( row : I n t, c o l : I n t, val : Double ) { e n t r i e s ( row * n + c o l ) = val } / * * Returns the product of t h i s and t h a t * / def * ( t h a t : M atrix ) : M atrix = { r e q u i r e ( n == t h a t. n ) val r e s u l t = new Matrix ( n ) for ( row < 0 u n t i l n ; column < 0 u n t i l n ) { var v = 0.0 for ( i < 0 u n t i l n ) v += this ( row, i ) * t h a t ( i, column ) r e s u l t ( row, column ) = v } r e s u l t } } Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 14 / 51

Θ-, Ω, o- ja ω-notaatiot O-notaation lisäksi yleisesti käytettyjä ovat seuraavat: f(n) = Ω(g(n)) jos g(n) = O(f(n)) f kasvaa vähintään yhtä nopeasti kuin g f(n) = Θ(g(n)) jos f(n) = O(g(n)) ja g(n) = O(f(n)) f ja g kasvavat yhtä nopeasti Lisäksi Määritelmä Merkitään f(n) = o(g(n)) jos kaikille vakioille c > 0 on olemassa vakio n 0 > 0 siten että f(n) < cg(n) kaikille n n 0. Jos f(n) = o(g(n)), niin f kasvaa hitaammin kuin g f(n) = ω(g(n)) jos g(n) = o(f(n)), f kasvaa nopeammin kuin g Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 15 / 51

Rekursio ja rekursioyhtälöt Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 16 / 51

Rekursiivisille ohjelmille asymptoottisen ajoajan analysointi suoraan (pseudo)koodista voi olla haastavampaa Esimerkki Ohjelmointi 2 -kurssilta: binäärihakualgoritmin toteutus Scalalla def binarysearch [ T <% Ordered [ T ] ] ( s : IndexedSeq [ T ], v : T ) : Boolean = { def i n n e r ( s t a r t : I n t, end : I n t ) : I n t = { i f (! ( s t a r t < end ) ) s t a r t else { val mid = s t a r t + ( ( end s t a r t ) / 2) val cmp = v compare s ( mid ) i f (cmp == 0) mid / / v == s ( mid ) else i f (cmp < 0) i n n e r ( s t a r t, mid 1) / / v < s ( mid ) else i n n e r ( mid+1, end ) / / v > s ( mid ) } } i f ( s. length == 0) false else s ( i n n e r ( 0, s. length 1)) == v } Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 17 / 51

Toisaalta ajoaika-analyysi vastaavasta ei-rekursiivisesta koodista voi olla vähintään yhtä haastavaa, jos ei vaikeampaakin... Esimerkki: ei-rekursiivinen versio binäärihakualgoritmista: def b i n a r y S e a r c h I t e r [ T <% Ordered [ T ] ] ( s : IndexedSeq [ T ], v : T ) : Boolean = { i f ( s. length == 0) return false var s t a r t = 0 var end = s. length 1 while ( s t a r t < end ) { val mid = s t a r t + ( ( end s t a r t ) / 2) val cmp = v compare s ( mid ) i f (cmp == 0) { s t a r t = mid ; end = mid} else i f (cmp < 0) end = mid 1 / / v < s ( mid ) else s t a r t = mid+1 / / v > s ( mid ) } return s ( s t a r t ) == v } Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 18 / 51

Rekursiivisten ohjelmien ajoajoille T (n) voidaan usein muodostaa rekursioyhtälö (engl. recurrence equation) muotoa missä T (n) =... n on parametri, joka liittyy usein jotenkin sen hetkisen rekursiivisen kutsun tarkasteleman osasyötteen kokoon tms, ja yhtälön oikea puoli riippuu rekursiivisesti pienempien osaongelmien ajoajasta T (n ) ja muista termeistä Lisäksi täytyy luonnollisesti ilmaista perustapaukset, yleensä T (0) tai T (1), jotka eivät riipu muista arvoista T (n ) Saatu rekursioyhtälö ratkaistaan jollain tavalla Huom: T (n) ei ole funktion/metodin laskema arvo vaan (arvio) sen ajoajasta syötteellä, jonka koko on n Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 19 / 51

Esimerkki Binäärihakualgoritmin rekursiivinen osa tarkastelee osataulukoita, joiden koko on n (end - start + 1 Scala-toteutuksessa). Kun n 1, niin rekursio päättyy n > 1, niin tehtdään alkioiden vertailu ja haku joko päättyy koska haettu alkio löytyi, tai kutsutaan samaa funktiota rekursiivisesti osataulukolla, jonka koko on n/2 Jos oletetaan taulukon alkioiden käsittely ja alkioiden vertailu vakioaikaisiksi operaatioiksi, niin saadaan rekursioyhtälöt T (0) = T (1) = d ja T (n) = c + T ( n/2 ) missä d ja c ovat vakioita, jotka kertovat indeksien vertailuun, alkioden hakemiseen jne menevän ajan Huomaa, että tässä T (n) on pahimman tapauksen arvio ajoajalle (suoritetuille käskyille tms) koska olemme jättäneet siinä huomioimatta tapauksen, jossa alkio löytyy taulukosta ennen kuin tarkasteltavan osataulukon koko on 1 Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 20 / 51

Ratkaistaan rekursioyhtälöt T (0) = T (1) = d ja T (n) = c + T ( n/2 ) Oletetaan, että n on kahden potenssi eli muotoa n = 2 k jollekin ei-negatiiviselle kokonaisluvulle k. Tällöin T (2 k ) = c + T (2 k 1 ) = c + (c + T (2 k 2 )) =... = kc + T (2 0 ) = ck + d Koska n = 2 k, joten k = log 2 n, ja c sekä d ovat vakioita, saadaan T (n) = T (2 k ) = ck + d = c log 2 n + d = O(log 2 n) Kun n ei ole kahden potenssi, niin T (n) T (2 log 2 n ) = d + c log 2 n d + c(1 + log 2 n) = O(log 2 n) koska T (n) on monotonisesti kasvava. Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 21 / 51

Käytettäessä O-notaatiota yksittäisiä vakioita voidaan usein poistaa jo alkuvaiheessa korvaamalla ne vakiolla 1. Binäärihakuesimerkissä rekursioyhtälö voisi siis olla T (0) = T (1) = 1 ja T (n) = 1 + T ( n/2 ). Tämän ratkaisuksi saadaan jälleen T (n) = O(log 2 n). Usein käytetään myös seuraavaa merkintää T (0) = T (1) = O(1) ja T (n) = O(1) + T ( n/2 ). Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 22 / 51

Ajoajan (suoritettujen käskyjen määrän) lisäksi myös muista algoritmien ominaisuuksista voidaan tehdä vastaavia analyyseja Jos esimerkiksi alkioiden vertailu ei olekaan vakioaikaista vaan vie enemmän resursseja, voi olla kiinnostavaa vertailla eri haku- ja järjestämisalgoritmien suorittamaa vertailujen määrää Esimerkki Binäärihaun suorittamien vertailujen asymptoottiselle lukumäärälle saadaan jälleen rekursioyhtälö T (0) = T (1) = 1 and T (n) = T ( n/2 ) + 1 missä n on syötetaulukon koko. Täten T (n) = O(log 2 n). Järjestämisalgoritmien yhteydessä seuraavalla kierroksella nähdään hieman monimutkaisempia rekursioyhtälöitä Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 23 / 51

Esimerkki: Potenssiin korottaminen neliöön korottamalla Tarkastellaan algoritmia, joka suorittaa potenssiin korottamisen x n mod 2 64 käyttämällä yhtälöä x n = k 1 i=0 x n i 2 i, missä n k 1 n k 2...n 1 n 0 on luvun n binääriesitys Esimerkiksi x 71 = x 1 x 2 x 4 x 64 = x 20 x 21 x 22 x 26 Algoritmi käyttää iteratiivista neliöön korottamista: alkaen arvosta x 20 = x, se muodostaa aina seuraavan neliön x 2i+1 = x 2i x 2i. def pow( x : Long, n : Long ) : Long = { r e q u i r e ( n >= 0) i f ( n == 0) 1 else i f ( n == 1) x else i f ( ( n & 0x01 ) == 1) x * pow( x * x, n / 2 ) else pow( x * x, n / 2 ) } Suhteessa muuttujan n arvoon saadaan ajoajalle rekursioyhtälöt T (0) = T (1) = 1 ja T (n) = 1 + T (n/2). Täten T (n) = O(log 2 n). Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 24 / 51

Esimerkki Tarkastellaan Ohjelmointi 2-kurssilta tuttua osajoukko-ongelmaa Annettuna joukko S = {v 1,...,v n } kokonaislukuja ja tavoiteluku t. Onko olemassa osajoukkoa S S siten, että v S v = t? ja (erittäin yksinkertaista) rekursiivista Scala-ohjelmaa sen ratkaisemiseksi: def subsetsum ( s : L i s t [ I n t ], t : I n t ) : Option [ L i s t [ I n t ] ] = { i f ( t == 0) return Some( N i l ) i f ( s. isempty ) return None val solnotincluded = subsetsum ( s. t a i l, t ) i f ( solnotincluded. nonempty ) return solnotincluded val s o l I n c l u d e d = subsetsum ( s. t a i l, t s. head ) i f ( s o l I n c l u d e d. nonempty ) return Some( s. head : : s o l I n c l u d e d. get ) return None } Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 25 / 51

Esimerkki: jatkuu Muodostetaan rekursiivisen ohjelman ajoaikaa kuvaavat rekursioyhtälöt parametrin n ollessa joukon alkioiden lukumäärä Pahimman tapauksen analyysissä ratkaisua ei löydy ja rekursio päättyy aina vasta kun tarkasteltava joukko on tyhjä Jokaisella rekursiivisella kutsukerralla ohjelma suorittaa joitain vakioaikaisia operaatioita ja sitten kaksi rekursiivista kutsua joukolla, josta on poistettu yksi alkio Täten saadaan rekursioyhtälöt T (0) = 1 ja T (n) = 1 + T (n 1) + T (n 1) = 1 + 2T (n 1) Kirjoitetaan yhtälö T (n) suoraviivaisesti auki ja saadaan T (n) = 1 + 2T (n 1) = 1 + 2(1 + 2T (n 2)) = 1 + 2 + 4(1 + 2T (n 3)) =... = n i=0 2i = 2 n+1 1 = Θ(2 n ) Eli pahimmassa tapauksessa ohjelman ajoaika on eksponentiaalinen Parhaassa (oletettavasti tosin hyvin harvinaisessa) tapauksessa tavoiteluku t on 0 ja ohjelman suoritus päättyy vakioajassa Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 26 / 51

Kokeellinen analyysi Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 27 / 51

Matemaattisen analyysin lisäksi voidaan (ja kannattaa) suorittaa myös kokeellista suorituskykyanalyysiä Voidaan vaikkapa ottaa kaksi toteutusta samasta tai eri algoritmista, jotka laskevat saman asian, ajaa näitä totetuksia jollain hyvin valitulla syötejoukolla ja vertailla mitattuja ajoaikoja Voitaisiin myös vaikkapa instrumentoida algoritmin toteutuskoodia niin, että se laskee esimerkiksi kaikki tehdyt vertailuoperaatiot ajon aikana ja kokeellisesti tarkastella saatujen määrien minimi-, keski- ja maksimiarvoja käyttää jotain profilointityökalua (kuten valgrind C/C++/konekieliohjelmille) saadaksemme selville välimuistiviittausten epäonnistumismäärät jne Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 28 / 51

Yksinkertainen ajoajan mittaaminen Kuten nähtiin Ohjelmointi 2-kurssilla, funktioiden ajoajan mittaus on periaatteessa melko helppoa 1 import java. lang. management.{ ManagementFactory, ThreadMXBean} package object t i m e r { val bean : ThreadMXBean = ManagementFactory. getthreadmxbean ( ) def getcputime = i f ( bean. iscurrentthreadcputimesupported ( ) ) bean. getcurrentthreadcputime ( ) else 0 def measurecputime [ T ] ( f : => T ) : ( T, Double ) = { val s t a r t = getcputime val r = f val end = getcputime ( r, ( end s t a r t ) / 1000000000.0) } def measurewallclocktime [ T ] ( f : => T ) : ( T, Double ) = { val s t a r t = System. nanotime val r = f val end = System. nanotime ( r, ( end s t a r t ) / 1000000000.0) } } 1 Prosessoriajan (CPU time) sijaan tulee käyttää seinäkelloaikaa (wall clock time) rinnakkaisohjelmien ajankäyttöä mitattaessa koska getcurrentthreadcputime mittaa vain yhden säikeen prosessoriaikaa Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 29 / 51

Valitettavasti asia ei ole ihan näin suoraviivainen tarkastellaan seuraavaa koodia, joka järjestää 20 taulukkoa, joissa jokaisessa 10000 kokonaislukua import timer. val a = new Array [ I n t ](10000) val rand = new scala. u t i l. Random ( ) val times = scala. c o l l e c t i o n. mutable. A r r a y B u f f e r [ Double ] ( ) for ( t e s t < 0 u n t i l 20) { for ( i < 0 u n t i l a. length ) a ( i ) = rand. n e x t I n t val (dummy, t ) = measurecputime { a. sorted } times += t } p r i n t l n ( times. mkstring (, ) ) Tuloksena saadaan seuraavat ajoajat 0.024590893, 0.010173347, 0.005155264, 0.003917635, 0.002668954, 0.002594947, Mitataan saman koodin suoritusta samankaltaisilla syötteillä mutta ajoaika tippuu kymmenesosaan joidenkin toistojen jälkeen? Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 30 / 51

Yllä oleva käytös voidaan selittää seuraavasti Scala-ohjelmat käännetään Java-tavukoodiksi Java-tavukoodia ajetaan Java-virtuaalikoneessa (Java virtual machine, JVM) Suoritus voi alkaa tulkitussa moodissa Osia koodista voidaan jossain vaiheessa kääntää alla olevan koneen konekoodiksi suorituksen aikana (just-in-time compilation) Käännettyä koodia voidaan optimoida ajon aikana Roskankeruuta (garbage collection) voi tapahtua ajon aikana Erityisesti lyhyiden ajoaikojen tulkinnassa kannattaa olla varovainen Pidempien ajoaikojen yhteydessä ilmiöt eivät tule suhteellisesti niin voimakkaasti esille ajoajan heilumisena Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 31 / 51

ScalaMeter Kuten nähtiin, tulkittujen ja virtuaalikoneissa ajettavien ohjelmien suorituskykyanalyysin tekeminen luotettavasti voi olla vaikeaa, erityisesti jos ajoajat ovat lyhyitä Tällä kurssilla käytetään joissain paikoin ScalaMeter-työkalua (https://scalameter.github.io/) Se yrittää ottaa nämä virtuaalikoneisiin ja roskankeräämiseen liittyvät piirteet huomioon mm. lämmittelemällä virtuaalikonetta ja käyttämällä tilastollisia menetelmiä Lisää tietoa löytyy työkalun dokumentaatiosta ja artikkelista A. Georges et al: Statistically rigorous java performance evaluation, Proc. OOPSLA 2007, pp. 57-76 Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 32 / 51

Joitakin perustietorakenteita Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 33 / 51

Kerrataan joitain tietorakenteita ja... tutustutaan joihinkin toisiin yksinkertaisiin lineaarisiin rakenteisiin Nämä eivät ole vain Scalassa käytettyjä rakenteita Tarkastellaan, minkä operaatioiden tehokasta toteutusta rakenteet tukevat Tällaista tehokkaiden operaatioiden joukkoa (ja niiden toiminnan määrittelyä) sanotaan usein abstraktiksi tietotyypiksi Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 34 / 51

Taulukot Sisäänrakennettu tyyppi useimmissa ohjelmointikielissä (Java-virtuaalikone, C/C++ jne) Sekvenssi, jossa n alkiota Vakioaikainen alkioiden luku ja kirjoitus Alkion etsiminen vie pahimmassa tapauksessa ajan Θ(n) (jos taulukkoa ei ole järjestetty binäärihakua varten tms) Taulukon koko n määrätään jo luomisvaiheessa, kun sille varataan yhtäjaksoinen muistijakso Alkioita ei voi lisätä tämän jälkeen alkuun tai loppuun taulukon kokoa kasvattaen. Jos näin tahdotaan tehdä, pitää varata uusi isompi taulukko ja kopioida vanha taulukko sekä uusi alkio siihen; tämän aikavaatimus on jälleen Θ(n) Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 35 / 51

Java- ja Scala-kielissä taulukon alkio ovat joko alkeistyyppejä (Int, Long jne) tai viittauksia olioihin, jotka sijaitsevat toisaalla muistissa Esimerkki: oliokaavio taulukolle kokonaislukuja (vasemmalla) ja taulukolle kokonaislukupareja (oikealla) Oikeanpuoleisen taulukon kolmas alkio on null-viittaus, joka ei viittaa yhteenkään olioon Toiset ohjelmointikielet, kuten C ja C++, sallivat taulukoiden sisältää myös rakenteellisia olioita Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 36 / 51

Dynaamiset taulukot ArrayBuffer Scalassa, vector C++-kielen standardikirjastossa Mahdollistavat kuoletetun vakioaikaisen alkioiden lisäämisen taulukon loppuun Idea: varataan hieman tarvetta suurempi taulukko a ja muistetaan taulukon a kapasiteetti c eli taulukon koko 2 ja taulukon tämänhetkinen koko s eli kuinka moni c:stä alkiosta on itse asiassa käytössä alla olevassa taulukossa a Alkion lisääminen dynaamisen taulukon loppuun: jos s < c, niin alkio kirjoitetaan taulukon a kohtaan s ja muuttujaa s kasvatetaan yhdellä, tai jos s = c, 1 varataan uusi taulukko kokoa c α, missä α > 1 on kasvukerroinvakio (yleensä 2), 2 kopioidaan vanhan taulukon alkiot uuden alkuun ja 3 asetetaan c = c α, kirjoitetaan alkio kohtaan s uudessa taulukossa ja kasvatetaan muuttujaa s ydellä 2 Java- ja Scala-kielissä tämä on sisäänrakennettu mutta esim. C/C++ -kielissä taulukot eivät sisällä mitään tämänkaltaista lisätietoa Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 37 / 51

Esimerkki: Alkion lisääminen täyteen dynaamiseen taulukkoon Luvun 21 lisääminen alla olevaan täyteen dynaamiseen taulukkoon käynnistää laajennusoperaation ja tuloksena saadaan Vanhan taulukon muistin vapauttamisesta huolehtii joko roskankeräämisalgoritmi (Java, Scala, Python) tai lisäyksen suorittava kirjastokoodi (C, C++ jne) Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 38 / 51

Esimerkki: alkioiden lisääminen ArrayBuffer-luokassa, lähdekoodia luokista ArrayBuffer ja ResizableArray / * * Appends a s i n g l e element to t h i s b u f f e r and r e t u r n s * the i d e n t i t y of the b u f f e r. I t takes constant amortized time. * / def +=(elem : A ) : this. type = { ensuresize ( size0 + 1) array ( size0 ) = elem. asinstanceof [ AnyRef ] size0 += 1 this } / * * Ensure t h a t the i n t e r n a l array has at l e a s t n c e l l s. protected def ensuresize ( n : I n t ) { * / val arraylength : Long = array. length / / Use a Long to prevent overflows i f ( n > arraylength ) { var newsize : Long = arraylength while ( n > newsize ) * 2 newsize = newsize * 2 / / Clamp newsize to I n t. MaxValue i f ( newsize > I n t. MaxValue ) newsize = I n t. MaxValue val newarray : Array [ AnyRef ] = new Array ( newsize. t o I n t ) scala. compat. Platform. arraycopy ( array, 0, newarray, 0, size0 ) array = newarray } } Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 39 / 51

Taulukon koon kasvattaminen kertoo sen koon vakiolla α ja täten taulukkojen koko kasvaa eksponentiaalisesti Eikö eksponentiaalinen kasvu ole huono asia? Ei tässä tapauksessa: jos vain lisäämme alkioita, niin taulukon täyttöaste on aina vähintään 1 α. Jos α = 2, niin tuhlataan vain korkeintaan sama määrä muistia mitä käytetään alkioiden tallentamiseen Verrataan tätä linkitettyihin listoihin, joissa jokainen solu sisältää ainakin yhden talletetun alkion ja viittauksen seuraavaan soluun listassa 3 linkitetyt listat tuhlaavat myös suurinpiirtein saman määrän muistia 3 Java- ja Scala-kielissä solu sisältää myös luokkatietoa Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 40 / 51

Miksi valittiin eksponentiaalinen kasvattaminen eikä vain taulukon koon kasvattamista jollain kohtuullisen isolla määrällä, esim. 1024, alkioita? Koska eksponentiaalinen kasvattaminen mahdollistaa lisäysten vaatiman ajoajan kuolettamisen vakioaikaiseksi Eli jos lisäämme n alkiota dynaamiseen taulukkoon, niin jotkut lisäämiset eli ne, joilla kasvattaminen tapahtuu, ovat ajoajallisesti kalliita koska joudutaan varaamaan muistia ja kopioimaan alkioita, mutta kokonaisajoaika kuoletettuna jokaiselle lisäämiselle on vakio. Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 41 / 51

Tarkastellaan jokaisella lisäyksellä tehtyjen muistioperaatioiden määrää (tämä on lineaarisessa suhteessa ajoaikaan) Oletetaan, että α = 2, ollaan jo lisätty n alkiota alkuaan tyhjään dynaamiseen taulukkoon ja taulukon kokoa joudutaan kasvattamaan kun lisätään seuraava alkio Niiden ylimääräisten muistioperaatioiden määrä, jotka joudutaan tekemään kopioitaessa edellisen taulukon alkioita uusiin tällä ja kaikilla edellisillä lisäyksillä yhteensä saadaan rekursioyhtälöstä T (d) = 0 ja T (n) = n + T (n/2), missä d on taulukon alkukoko. Nyt T (n) n + T (n/2) n + n/2 + n/4 + n/8... 2n Lisätään tähän vielä 1 muistioperaatio per lisäys (uuden alkion kirjoittaminen) ja saadaan, että muistioperaatioiden määrä kuoletettuna jokaiselle lisäykselle on 2n+n = 3 n Vastaavasti jokaiselle kasvukerroinvakiolle α > 1 saataisiin lisäkustannukseksi korkeintaan n i=0 1 = n 1 = n α = O(n) ja α i 1 α 1 α 1 kuoletettu kustannus täten jälleen vakio Esim. kun α = 1.1, niin kuoletettu kustannus lisäyksille on 11 + 1 = 12 muistioperaatiota Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 42 / 51

Kasvukerroinvakiolle 2 voidaan edellä esitettyä analyysiä visualisoida graafisesti kun lisätään 9 alkiota dynaamiseen taulukkoon, jonka koko on alussa 1: Siniset pallurat ovat normaaleja muistiperaatioita, jotka johtuvat uuden alkio kirjoittamisesta, punaiset edellisten laajennusten kopiointioperaatioista ja pinkit nykyisen laajennuksen kopioinnista Jos romautetaan punaiset tornit oikealle ja pinkki vasemmalle, saadaan mistä näkyy, että jokaiselle lisäykselle kuoleentuu 3 palluraa Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 43 / 51

Tulisiko dynaamisen taulukon kokoa supistaa jos siinä on, poistojen jälkeen, vähän alkioita kapasiteettiin nähden? Kyllä, tämä voidaan tehdä niin, että sekä taulukon loppuun tehtyjen lisäysten että poistojen kuoletettu ajoaika pysyy vakiona Mutta supistuksen käynnistävä täyttöasteen s ala-arvo saa olla c korkeintaan kasvukerroinvakion käänteisluku. Eli esim. jos taulukon kokoa kasvatetaan kaksinkertaiseksi lisäyksen yhteydessä ja sen jälkeen heti poistetaan alkio lopusta, niin taulukon kokoa ei tule supistaa heti Jos kasvukerroinvakio on 2.0 ja supistusvakio 0.25, niin lisäys- ja poisto-operaatiot ovat kuoletetusti vakioaikaisia, ks. luku 17.4 kirjassa Introduction to Algorithms, 3rd ed. (online via Aalto lib) Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 44 / 51

Linkitetyt listat Kertausta Ohjelmointi 2-kurssin rekursio -kierrokselta: muuttumattomat linkitetyt listat Listoja ei muokata luomisen jälkeen, solut voivat olla jaettuja usean listan kesken Alkioiden lisääminen alkuun on vakioaikaista, loppuun lineaariaikaista Esimerkki: Muutumattomat linkitetyt listat Linkitetty lista l: Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 45 / 51

Linkitetyt listat Kertausta Ohjelmointi 2-kurssin rekursio -kierrokselta: muuttumattomat linkitetyt listat Listoja ei muokata luomisen jälkeen, solut voivat olla jaettuja usean listan kesken Alkioiden lisääminen alkuun on vakioaikaista, loppuun lineaariaikaista Esimerkki: Muutumattomat linkitetyt listat Lista k, joka on listan l häntä : Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 45 / 51

Linkitetyt listat Kertausta Ohjelmointi 2-kurssin rekursio -kierrokselta: muuttumattomat linkitetyt listat Listoja ei muokata luomisen jälkeen, solut voivat olla jaettuja usean listan kesken Alkioiden lisääminen alkuun on vakioaikaista, loppuun lineaariaikaista Esimerkki: Muutumattomat linkitetyt listat Lista m, joka saatu listasta l lisäämällä 11 alkion 21 jälkeen: Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 45 / 51

Linkitetyistä listoista on myös muuttuvatilainen versio Soluja ei nyt jaeta eri listojen kesken Alkioiden lisäys listaan voidaan tehdä varaamalla uusi solu ja muokkaamalla listan solujen viittauksia Jos viittaus listan viimeiseen soluun pidetään muistissa, voidaan myös listan loppuun lisätä alkioita vakioajassa Listan alkioiden lukumäärää voidaan myös pitää muistissa, jolloin pituuskyselyt ovat vakioaikaisia Esimerkki: Muuttuvatilaiset linkitetyt listat Muuttuvatilainen linkitetty lista: Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 46 / 51

Linkitetyistä listoista on myös muuttuvatilainen versio Soluja ei nyt jaeta eri listojen kesken Alkioiden lisäys listaan voidaan tehdä varaamalla uusi solu ja muokkaamalla listan solujen viittauksia Jos viittaus listan viimeiseen soluun pidetään muistissa, voidaan myös listan loppuun lisätä alkioita vakioajassa Listan alkioiden lukumäärää voidaan myös pitää muistissa, jolloin pituuskyselyt ovat vakioaikaisia Esimerkki: Muuttuvatilaiset linkitetyt listat Sama lista sen jälkeen kun on lisätty 11 alkion 21 jälkeen: Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 46 / 51

Kahteen suuntaan linkitetyt listat ovat edellisen laajennus, jossa jokainen solu sisältää myös viittauksen edelliseen soluun Tällaisia listoja voidaan käydä läpi tehokkaasti myös käänteisessä järjestyksessä Esimerkki: Muuttuvatilainen kahteen suuntaan linkitetty lista Edellisen esimerkin listan kahteen suuntaan linkitetty versio: Jos saadaan viittaus johonkin listan soluun, niin solun poistaminen samoin kuin alkion lisääminen juuri ennen sitä tai sen jälkeen on helppoa johtuen viittauksista edeltävään ja seuraavaan soluun Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 47 / 51

Muuttuvatilaiset lista Scala-kielessä: ListBuffer (lähdekoodi) Aiemmat versiot LinkedList ja DoubleLinkedList eivät ole enää suositeltuja C++-kielessä luokka list toteuttaa kahteen suuntaan linkitetyt listat Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 48 / 51

Jonot Jonot (engl. queue) viittaavat yleensä ensin sisään, ensin ulos -jonoihin (engl. first-in-first-out, FIFO) Perusoperaatiot ovat enqueue, joka lisää alkion jonon loppuun ja dequeue, joka poistaa alkion jonon alusta Tällaiset jonot on helppo toteuttaa kahteen suuntaan linkitetyillä listoilla niin, että sekä enqueue- ja dequeue-operaatiot ovat vakioaikaisia Scalassa muutuvatilainen Queue on toteutettu linkitetyillä listoilla (sisäinen MutableList-luokka) C++-kielen standardikirjastossa queue-luoka toteuttaa jonot ( enqueue on push ja dequeue on pop ) Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 49 / 51

Pinot Pinot ovat jonojen tapaisia mutta viimeksi sisään, ensin ulos -jonokurilla (engl. last-in-first-out, LIFO) Perusoperaatiot ovat push, joka laittaa alkion pinon päälle ja pop, joka poistaa pinon päällimmäisen alkion Pinot on myös samoin helppo toteuttaa linkitettyjen listojen tai dynaamisten taulukoiden avulla Scalassa sekä muuttumattomat että muuttuvatilaiset pinot on toteutettu listojen avulla C++-kielessä: stack-luokka Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 50 / 51

Lisää tietorakenteita seuraavilla kierroksilla! Tommi Junttila (Aalto University) Kierros 1 CS-A1140 / Autumn 2017 51 / 51