3. Pinot, jonot ja linkitetyt listat

Samankaltaiset tiedostot
Pino S on abstrakti tietotyyppi, jolla on ainakin perusmetodit:

3. Pinot, jonot ja linkitetyt listat

3. Pinot, jonot ja linkitetyt listat

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

A TIETORAKENTEET JA ALGORITMIT

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

18. Abstraktit tietotyypit 18.1

Algoritmit 1. Luento 4 Ke Timo Männikkö

811312A Tietorakenteet ja algoritmit II Perustietorakenteet

TIETORAKENTEET JA ALGORITMIT

Tietorakenteet ja algoritmit

4. Sekvenssit Astetta soveltavat sekvenssit

Algoritmit 1. Luento 3 Ti Timo Männikkö

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

Tietorakenteet, laskuharjoitus 3, ratkaisuja

2. Perustietorakenteet

815338A Ohjelmointikielten periaatteet Harjoitus 5 Vastaukset

Tieto- ja tallennusrakenteet

Sekvenssi: kokoelma peräkkäisiä alkioita (lineaarinen

Listarakenne (ArrayList-luokka)

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

Abstraktit tietotyypit. TIEA341 Funktio ohjelmointi 1 Syksy 2005

Algoritmit 1. Demot Timo Männikkö

Ohjelmointi 2 / 2010 Välikoe / 26.3

Algoritmit 2. Luento 2 To Timo Männikkö

815338A Ohjelmointikielten periaatteet Harjoitus 3 vastaukset

Algoritmit 1. Luento 6 Ke Timo Männikkö

Java kahdessa tunnissa. Jyry Suvilehto

Algoritmit ja tietorakenteet / HL Copyright Hannu Laine

Ohjelmoinnin peruskurssien laaja oppimäärä

Olion elinikä. Olion luominen. Olion tuhoutuminen. Olion tuhoutuminen. Kissa rontti = null; rontti = new Kissa();

Algoritmit 2. Luento 2 Ke Timo Männikkö

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

Ohjelmoinnin peruskurssien laaja oppimäärä

A215 Tietorakenteet. Tietojenkäsittelytieteiden laitos Tampereen yliopisto. Periodit I-II, syksy 2007

14. Poikkeukset 14.1

JAVA-PERUSTEET. JAVA-OHJELMOINTI 3op A JAVAN PERUSTEET LYHYT KERTAUS JAVAN OMINAISUUKSISTA JAVAN OMINAISUUKSIA. Java vs. C++?

Tietorakenteet ja algoritmit

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

Rinnakkaisohjelmointi kurssi. Opintopiiri työskentelyn raportti

Tietorakenteet ja algoritmit Johdanto Lauri Malmi / Ari Korhonen

Tietorakenteet ja algoritmit

3. Binääripuu, Java-toteutus

815338A Ohjelmointikielten periaatteet Harjoitus 6 Vastaukset

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

Java-API, rajapinnat, poikkeukset, UML,...

Rajapinta (interface)

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

Algoritmit 2. Luento 7 Ti Timo Männikkö

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

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Rakenteiset tietotyypit Moniulotteiset taulukot

Jakso 4 Aliohjelmien toteutus

Tietorakenteet ja algoritmit

815338A Ohjelmointikielten periaatteet Harjoitus 7 Vastaukset

Ohjelmoinnin perusteet Y Python

T Henkilökohtainen harjoitus: FASTAXON

Sisällys. 7. Oliot ja viitteet. Olion luominen. Olio Java-kielessä

14. Poikkeukset 14.1

7. Oliot ja viitteet 7.1

Helsingin yliopisto, Tietojenkäsittelytieteen laitos Ohjelmistotuotanto, kurssikoe , H. Laine Arvostelu

Sisällys. 14. Poikkeukset. Johdanto. Johdanto

Tietorakenteet, laskuharjoitus 4,

812341A Olio-ohjelmointi Peruskäsitteet jatkoa

Tietorakenteet ja algoritmit

811312A Tietorakenteet ja algoritmit Kertausta kurssin alkuosasta

Tietorakenteet ja algoritmit - syksy

Ohjelmassa henkilön etunimi ja sukunimi luetaan kahteen muuttujaan seuraavasti:

Yleistä. Nyt käsitellään vain taulukko (array), joka on saman tyyppisten muuttujien eli alkioiden (element) kokoelma.

Algoritmit 1. Demot Timo Männikkö

Sisältö. 2. Taulukot. Yleistä. Yleistä

Ohjelmointi 2 / 2008 Välikoe / Pöytätestaa seuraava ohjelma.

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

Informaatioteknologian laitos Olio-ohjelmoinnin perusteet / Salo

15. Ohjelmoinnin tekniikkaa 15.1

16. Javan omat luokat 16.1

815338A Ohjelmointikielten periaatteet Harjoitus 4 vastaukset

Ohjelmoinnin perusteet Y Python

1.3 Lohkorakenne muodostetaan käyttämällä a) puolipistettä b) aaltosulkeita c) BEGIN ja END lausekkeita d) sisennystä

Luento 4 Aliohjelmien toteutus

ITKP102 Ohjelmointi 1 (6 op)

Ohjelmoinnin jatkokurssi, kurssikoe

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.

Aikavaativuuden perussäännöt

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

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

Ohjelmoinnin perusteet Y Python

15. Ohjelmoinnin tekniikkaa 15.1

Olio-ohjelmointi Syntaksikokoelma

A TIETORAKENTEET JA ALGORITMIT

Aliohjelmatyypit (2) Jakso 4 Aliohjelmien toteutus

Java-kielen perusteet

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

Geneeriset luokat. C++ - perusteet Java-osaajille luento 6/7: Template, tyyppi-informaatio, nimiavaruudet. Geneerisen luokan käyttö.

Sisällys. 1. Omat operaatiot. Yleistä operaatioista. Yleistä operaatioista

Se mistä tilasta aloitetaan, merkitään tyhjästä tulevalla nuolella. Yllä olevassa esimerkissä aloitustila on A.

Opintojakso TT00AA11 Ohjelmoinnin jatko (Java): 3 op Rajapinnat ja sisäluokat

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

1. Omat operaatiot 1.1

ITKP102 Ohjelmointi 1 (6 op)

Transkriptio:

3 Pinot, jonot ja linkitetyt listat Pinot ja jonot ovat yksinkertaisimpia tietorakenteita, mutta myös tärkeimpiä Niitä käytetään lukuisissa sovelluksissa ja monimutkaisten tietorakenteiden osina Yksinkertaisuutensa vuoksi ne ovat niitä harvoja, joita on toteutettu myös laitteistotason mikrokäskyissä keskusyksikkötasolla 31 Pinot Pino (stack) sisältää alkioita, joita lisätään ja poistetaan pinon huipulta viimeksi-pinoon-ensiksi-pinosta -periaatteen mukaan (LIFO last-in-first-out) Alkioita voidaan lisätä koska tahansa, mutta poistaa voidaan kulloinkin ainoastaan viimeksi pinoon lisätty Tietorakenteen pino perusoperaatiot ovat pinoa ja pura lisäämistä ja poistamista varten (push ja pop) 3 kappale 58 Esim 31 Verkkoselain tallettaa käydyt osoitteet pinoon Käyttäjän tullessa uuteen verkko-osoitteeseen kyseinen osoite työnnetään osoitepinoon Selain sallii käyttäjän purkaa pinoa takaisin viittaavalla painikkeella Esim 32 Monipuoliset tekstinkäsittelyjärjestelmät käsittävät nykyään aina käskyn peruutuksen (undo), joka toteutetaan pinon avulla, kun pinoon talletaan tehdyt muutokset Pino abstraktina tietotyyppinä Pino S on abstrakti tietotyyppi, joka käsittää seuraavat perusmetodit: push(o): Lisää alkion o pinon huipulle Syöte: alkio 3 kappale 59 pop(): Antaa päällimmäisen alkion poistaen sen samalla pinosta; virhe seuraa, mikäli pino oli tyhjä Tulos: alkio Lisäksi voidaan käyttää pino-operaatioita: Esim 33 Oheinen taulukko esittää sarjan pino-operaatioita ja näiden vaikutuksen alunperin tyhjään pinoon S, johon talletetaan yksinkertaisuuden vuoksi kokonaislukuja size(): Palauttaa pinon alkioiden määrän Tulos: kokonaisluku isempty(): Palauttaa totuusarvon sen mukaan, onko pino tyhjä vai ei Tulos: totuusarvo Top(): Palauttaa pinon huippualkion poistamatta sitä pinosta; virhe seuraa pinon ollessa tyhjä Tulos: alkio 3 kappale 60 operaatio tulos S push(5) - (5) push(3) - (5,3) pop() 3 (5) push(7) - (5,7) pop() 7 (5) top() 5 (5) 3 kappale 61

pop() 5 () pop() virhe () isempty() tosi () push(9) - (9) push(7) - (9,7) push(3) - (9,7,3) push(5) - (9,7,3,5) size() 4 (9,7,3,5) pop() 5 (9,7,3) 3 kappale 62 Pino on sisäänrakennettuna luokkana Javassa Tärkeimmät metodit ovat push, pop, peek (vastaa top-operaatiota), size ja empty Seuraavana (koodi 31) esitetään yleisellä tasolla (mielivaltaisesti valittuja olioita) rajapintaluokka vastaamaan pinon abstraktia tietotyyppiä Mukana on lisäksi virheenkäsittelymetodeita pop ja top varten pinon ollessa tyhjä (koodi 32) public interface Stack { // accessor methods public int size(); // return the number of elements stored in the stack public boolean isempty(); // test whether the stack is empty public Object top() // return the top element throws StackEmptyException; // thrown if called on an empty stack // update methods public void push (Object element); // insert an element onto the stack public Object pop() // return and remove the top element of the stack throws StackEmptyException; // thrown if called on an empty stack Koodi 31 Pinon rajapintaluokka 3 kappale 63 public class StackEmptyException extends RuntimeException { public StackEmptyException(String err) { super(err); S 0 1 2 t N-1 Koodi 32 Pinon metodien pop ja top poikkeusten käsittely Yksinkertainen taulukkoon perustuva toteutus Kuvataan pinon toteutus, jossa alkiot talletetaan taulukkoon Kun taulukon koko on määrättävä se luotaessa, annetaan sille jokin maksimikoko N Niinpä pino S sisältää N-alkioisen taulukon ja kokonaislukumuuttujan t, joka osoittaa pinon huippualkion paikan taulukossa (kuva 31) Kuva 31 Pinon toteutus taulukkona S, jossa huippualkio on paikassa S[t] Pinon indeksimuuttuja alustetaan arvoksi -1, koska Javassa taulukot alkavat indeksin arvosta 0 Lisäksi annetaan uusi poikkeus pinon ylitäytön estämiseksi Nyt voidaan esittää abstrakti tietotyyppi pseudokoodina 3 kappale 64 3 kappale 65

Algorithm size(): return t + 1 Algorithm isempty(): return (t < 0) Algorithm top(): if isempty() then throw StackEmptyException return S[t] Koodi 33 Pinon abstraktia tietotyyppiä Algorithm push(o): if size()=n then throw StackFullException t t + 1 S[t] o Algorithm pop(): if isempty() then throw StackEmptyException e S[t] S[t] null t t - 1 return e Koodi 33 (jatkoa) Pinon abstraktia tietotyyppiä 3 kappale 66 3 kappale 67 Kielestä riippuen automaattista roskienkeräystä voisi käyttää sen sijaan, että tässä asetettiin poistetun alkion paikalle null-arvo, mutta tehtävä on kuitenkin hoidettava tavalla tai toisella Esitetyt pinometodit toimivat kaikki kompleksisuudeltaan ajassa O(1), ts ovat riippumattomia alkioiden määrästä pinossa n tai taulukon koosta N Tilavaatimus on luonnollisesti O(N) Tietorakenne pino on käytössä lukuisissa sovelluksissa Sen taulukkototeutus on erittäin yksinkertainen ja tehokas Täysin ideaalinen se ei kuitenkaan ole, kun taulukon koko on rajoitettu Myöhemmin tässä kappaleessa tarkastellaan taulukkoa dynaamisempaa toteutustapaa Seuraavana on pinon Javatoteutus public class ArrayStack implements Stack { // Implementation of the Stack interface using an array public static final int CAPACITY = 1000; // default capacity of the stack private int capacity; // maximum capacity of the stack private Object S[]; // S holds the elements of the stack private int top = -1; // the top element of the stack public ArrayStack() { // Initialize the stack with default capacity this(capacity); public ArrayStack(int cap) { // Initialize the stack with given capacity capacity = cap; S = new Object[capacity]; Koodi 34 Pinon taulukkototeutusta Javalla 3 kappale 68 3 kappale 69

public int size() { // Return the current stack size return (top + 1); public boolean isempty() { // Return true iff the stack is empty return (top < 0); public void push(object obj) { // Push a new object on the stack if (size() == capacity) throw new StackFullException("Stack overflow"); S[++top] = obj; Koodi 34 (jatkoa) Pinon taulukkototeutusta Javalla public Object top() // Return the top stack element throws StackEmptyException { if (isempty()) throw new StackEmptyException("Stack is empty"); return S[top]; public Object pop() // Pop off the stack element throws StackEmptyException { Object elem; if (isempty()) throw new StackEmptyException("Stack is Empty"); elem = S[top]; S[top--] = null; // Dereference S[top] and decrement top return elem; 3 kappale 70 Koodi 34 (jatkoa) Pinon taulukkototeutusta Javalla 3 kappale 71 Javan käyttö abstraktin tietotyypin toteutuksessa mahdollistaa geneeristen olioiden tallettamisen pinoon niiden kuuluessa mielivaltaisesti valittuihin luokkiin, kuten luokkiin kokonaisluvut tai opiskelijat Jotta saadaan alkio määrätyn luokan ilmentymänä, on suoritettava tyypinmuunnos (cast), jonka avulla olio nähdään määrättyyn luokkaan kuuluvana eikä yleisempänä, yliluokkaan kuuluvana (Object tässä) Tämä esitetään eräälle tapaukselle seuraavassa public static Integer[ ] reverse(integer[ ] a) { ArrayStack S = new ArrayStack(alength); Integer[ ] b = new Integer[alength]; for (int i=0; i<alength; i++) Spush(a[i]); for (int i=0; i<alength; i++) b[i] = (Integer) (Spop()); return b; Koodi 35 Kuvassa on metodi, joka kääntää alkioiden järjestyksen taulukossa aputaulukkoa käyttäen Tyypinmuunnos suoritetaan pakottamalla pop-metodin palauttama olio kokonaislukutyyppiseksi Java antaa muuttujan eli kentän length (taulukon koko) käyttöön 3 kappale 72 3 kappale 73

Pinoja Javan virtuaalikoneessa Java-ohjelma käännetään välikieleen, jota ajetaan Javan virtuaalikoneessa - näin saavutetaan laiteriippumattomuus Pinot ovat tärkeä osa Java-ohjelman suoritusaikaista ympäristöä Javan metodipinossa pidetään kirjaa lokaaleista muuttujista ja muusta tärkeästä metodien tiedosta näitä kutsuttaessa (kuva 33) Virtuaalikone ylläpitää ohjelmalaskuria, rekisteriä, jossa on kulloinkin suoritettavana olevan metodin osoite Virtuaalikone ylläpitää pinoa, jonka alkiot ovat sillä hetkellä aktiivisten metodikutsujen tiedot Päällimmäisenä pinossa on suorittevana oleva metodi ja sen alapuolella tilapäisesti keskeytetyt Nämä muodostavat kutsujen ketjun, jossa järjestyksen määrää se, miten ne on pinoon asetettu eli metodeja kutsuttu 3 kappale 74 Java käyttää arvoparametreja, mikä merkitsee, että muuttujan tai lausekkeen nykyinen arvo välitetään kutsutulle metodille Jos kutsuttu metodi muuttaa lokaalin muuttujan arvoa, se ei muuta kutsuneen vastaavan muuttujan arvoa Kuvassa 33 Metodi cool on juuri kutsunut metodia fool ja edellistä sitä ennen metodi main Ohjelmalaskurin (PC) arvo, parametrit ja lokaalit muuttujat on talletettu pinon alkioihin Metodipinoa käytetään tässä metodikutsujen toteutukseen ja parametrinvälitykseen Tämä ei ole toki vain Javan erityispiirre, vaan vastaavia on ollut jo kauan käytössä ohjelmointikielten ajoaikaisissa ympäristöissä 3 kappale 75 fool: PC = 320 m = 7 cool: PC = 216 j = 5 k = 7 main: PC = 14 i = 5 Javan pino Kuva 33 Esimerkki Javan pinosta main() { int i=5; 14 cool(i); cool(int j) { int k=7; 216 fool(k); 320 fool(int m) { Java-ohjelma 3 kappale 76 Yhtenä pinon käytön etuna on rekursion ohjelmoinnin tekeminen helpoksi Tällöin metodi voi kutsua itseään ikään kuin aliohjelmana Rekursio on varsin hyödyllinen, koska se sallii yksinkertaisten ja tehokkaiden ohjelmien suunnittelun melko vaikeille ongelmille Eräs tyypillinen (hyvin yksinkertainen) esimerkki on kertomafunktion n! = n(n-1)(n-2) 1 laskeminen seuraavasti public static long factorial(long n) { if (n<=1) return 1; else return n*factorial(n-1); Koodi 36 Rekursiivisesti kertomaa laskeva ohjelma 3 kappale 77

Ohjelma kutsuu itseään rekursiivisesti, kunnes parametrin arvoksi tulee 1, jolloin tämä palautetaan ja kerrotaan 2:n kanssa jne Tässä nähdään myös se tärkeä yksityiskohta, että rekursiivisen metodin pitää aina käsittää metodin lopetus, sillä muuten aiheutuisi äärettömästi rekursiivinen metodi, joka päätyisi lopulta suoritusaikaiseen virheeseen pinolle varatun tilan loppuessa Javan virtuaalikone soveltaa myös operandipinoa, jota käytetään aritmeettisten lausekkeiden, kuten ((a+b)*(c+d))/e, laskennassa Yksinkertainen binäärioperaatio, esim a+b, suoritetaan pinoamalla symboli a operandipinoon, samoin symboli b ja sitten kutsumalla käskyä, joka purkaa nämä kaksi pinon päällimmäistä alkiota laskien kyseisen binäärioperaation niille ja pinoaa tuloksen takaisin operandipinon huippualkioksi 32 Jonot Jono (queue) on pinoa muistuttava tärkeä tietorakenne Jonossa alkioita lisätään ja poistetaan ensiksi-sisään-ensiksi-ulos - periaatteen (FIFO first-in first-out) mukaisesti Alkioita voidaan lisätä miloin vain, mutta poistaa voidaan ainoastaan se, joka on ollut kauimmiten jonossa Tämä esitetään niin, että alkiot lisätään jonon loppuun ja niitä poistetaan sen alusta Jonon abstrakti tietotyyppi Jonon abstrakti tietotyyppi määritellään sellaisena, jossa alkion saanti ja poistaminen on rajoitettu jonon ensimmäiseen alkioon ja lisäys loppuun Se käsittää kaksi perusmetodia: 3 kappale 78 3 kappale 79 enqueue(o): Lisää olion o jonon loppuun Syöte: olio dequeue(): Poistaa ja palauttaa alkion jonon alusta Tulos: olio Yleensä sisällytetään jonon abstraktiin tietotyyppiin vielä: size(): Palauttaa jonon alkioiden määrän Tulos: kokonaisluku isempty(): Palauttaa totuusarvon siitä, onko jono tyhjä Tulos: totuusarvo front(): Palauttaa, mutta ei poista, jonon ensimmäisen alkion; virhe tapahtuu jonon ollessa tyhjä Tulos: alkio 3 kappale 80 Tietorakenteella jono on lukuisia sovellusmahdollisuuksia Esimerkkinä voisi olla erilaiset varausjärjestelmät, joissa asiakkaita palvellaan tulojärjestyksessä Kysymyksessä on tapahtumien (transaktio) käsittely Esim 34 Seuraava taulukko kuvaa sarjan jono-operaatioita ja näiden vaikutuksen aluksi tyhjään kokonaislukujen jonoon Q operaatio tulos Q enqueue(5) - (5) enqueue(3) - (5,3) dequeue() 5 (3) enqueue(7) - (3,7) 3 kappale 81

dequeue() 3 (7) front() 7 (7) dequeue() 7 () dequeue() virhe () isempty() tosi () enqueue(9) - (9) enqueue(7) - (9,7) size() 2 (9,7) enqueue(3) - (9,7,3) enqueue(5) - (9,7,3,5) dequeue() 9 (7,3,5) Jonon abstraktin tietotyypin Java-kielinen rajapinta on esitetty oheisena, joka sallii mielivaltaisesti valittujen luokkien olioita lisättävän jonoon public interface Queue { // accessor methods public int size(); // return the number of elements stored in the queue public boolean isempty(); // test whether the queue is empty public Object front() // return the front element of the queue throws QueueEmptyException; // thrown if called on an empty queue // update methods public void enqueue (Object element); // insert an element at the rear public Object dequeue() // return and remove the front element throws QueueEmptyException; // thrown if called on an empty queue Koodi 37 Jonon rajapinta 3 kappale 82 3 kappale 83 Jonon yksinkertainen taulukkopohjainen toteutus Kiinnitetään jälleen aluksi taulukon koko arvoksi N Kun jono noudattaa FIFO-periaatetta, huomiota pitää kiinnittää, miten hallitaan jonon alkua ja loppua Yksi mahdollisuus olisi muuntaa pinoa niin, että jonon alku olisi paikassa Q[0], josta lukien jono kasvaisi taulukon loppua kohti Tämä olisi kuitenkin huono ratkaisu, koska se vaatisi melkein kaikkien (n-1) alkioiden siirtämistä poistettaessa (dequeue) jonon alusta, joka on Θ(n) n:lle alkiolle jonossa Poisto voidaan tehdä vakioajassa määriteltäessä kaksi muuttujaa, f ja r, seuraavasti: Jonon Q ensimmäinen alkio osoitetaan indeksillä f Se on seuraava poistettava, ellei jono ole tyhjä, jolloin f=r Jonon Q seuraavan vapaan paikan osoittaa indeksi r 3 kappale 84 Aluksi määritellään f = r = 0 eli jono tyhjäksi Poistettaessa alkio yksinkertaisesti lisätään muuttujan f arvoa ykkösellä, ja lisättäessä alkio lisätään muuttujan r arvoa ykkösellä eli seuraavaan vapaseen paikkaan Näin operaatiot sekä dequeue että enqueue ovat O(1) aikakompleksisuudeltaan Erikoistapauksina jää käsiteltäviksi tilanteet, joissa tullaan jonon alkuun ja loppuun ja pitää kuitenkin tehdä vielä lisäyksiä tai poistoja Tämä hallitaan helposti määriteltäessä taulukko rengasrankenteena, jossa jono jatkuu alkiosta Q[N-1] alkioon Q[0] ja päinvastoin (kuva 34) Toteutus on helppo käytettäessä modulo-laskentaa, (f+1) mod N tai (r+1) mod N, operaatiosta riippuen Modulo-operaattori mod (Javassa %) tuottaa kokonaislukujaon jakojäännöksen eli (y 0) x mod y = x - x/y y 3 kappale 85

Q Q 0 1 2 f r N-1 (a) 0 1 2 r f N-1 (b) Kuva 34 Taulukon Q käyttö rengasrakenteena: (a) normaali tilanne, kun f r, ja (b) kierretty tilanne, kun r f Käytössä olevat paikat on väritetty Ongelmana on hallita tilanne, kun taulukko on täyttymässä eli N alkiota haluttaisiin tallettaa ja tällöin f=r Tämä on hankalasti samankaltainen kuin jonon ollessa tyhjä Yksinkertaisin tapa väistää ongelma on rajoittaa talletettavien alkioiden määräksi N- 1 Tämä voidaan testata ehdolla (N-f+r) mod N sekä normaalissa että kierretyssä tapauksessa (kuva 34) 3 kappale 86 Seuraavaksi esitetään pseudokoodinen kuvaus jono-operaatioille Kunkin niistä aikavaatimus on vakio, O(1) Muistitilavaatimus on pinon tapaan (riippumaton arvosta n N) O(N) Algorithm size(): return (N-f+r) mod N Algorithm isempty(): return (f=r) Algorithm front(): if isempty() then throw QueueEmptyException return Q[f] Koodi 38 Jonon taulukkototeutus pseudokoodisena 3 kappale 87 Algorithm dequeue(): if isempty() then throw QueueEmptyException temp Q[f] Q[f] null f (f+1) mod N return temp Algorithm enqueue(o): if size()=n-1 then throw QueueFullException Q[r] o r (r+1) mod N Koodi 38 (jatkoa) Jonon taulukkototeutus pseudokoodisena Jonon heikkous on sama kuin pinon Alkioiden määrä on rajoitettu Jos alkioiden maksimimäärästä on hyvä arvio, ei rajoituksella ole kuitenkaan suurta merkitystä Muistin hallinta Javassa Javassa oliolle voidaan varata muistia dynaamisesti metodin suoritusaikana, kun metodi hyödyntää Javan käskyä new Esim voidaan luoda 12-alkioinen olio Vector käskyllä: Vector items = new Vector(12) Tätä oliota voidaan käyttää taulukon tapaan paitsi, että sen koko kasvaa ja kutistuu tarpeen mukaan Lisäksi se ei häviä, vaikka sen luonut metodi päätetään 3 kappale 88 3 kappale 89

Pinon sijasta Javassa sovelletaan jonoa olioiden muistinhallinnassa Muistia (kuva 35) varataan muistikasasta tai -keosta (heap), jota ei pidä sekoittaa myöhemmin esiteltävään kasa tai keko nimiseen tietorakenteeseen Tämä muisti jaetaan lohkoihin (block), jotka ovat peräkkäisiä muistialueita Java soveltaa jono-operaatioita näille lohkoille (koko esim 1024 tavua) Muitakin jonosovelluksia Javassa on olemassa Säikeiden (thread) käyttö rajoitetun moniohjelmoinnin mahdollistamiseksi hyödyntää jonoa, missä säie sijoitetaan jonon alkioon ja sille annetaan aikaviipaleita vuorotellen muiden jonossa olevien säikeiden kanssa 33 Linkitetyt listat ohjelman koodi Javan pino vapaa muisti muistikasa Kuva 35 Javan virtuaalikoneen muistin käyttöä Edellä tarkasteltiin pinojen ja jonojen abstrakteja tietotyyppejä taulukkototeutuksin Vaikka toteutukset olivat sangen yksinkertaisia, ne eivät olleet joustavia ratkaisuja, koska taulukon koko on kiinnitettävä etukäteen Linkitetty lista tarjoaa tälle joustavan vaihtoehdon 3 kappale 90 3 kappale 91 Yhteen suuntaan linkitetty lista Linkitetty tai ketjutettu lista on yksinkertaisimmillaan lineaarisesti järjestettyjen solmujen kokoelma Kukin solmu käsittää varsinaisen tiedon lisäksi viittauksen tai osoittimen seuraavaan (next) solmuun (kuva 36) pää seuraaja seuraaja seuraaja seuraaja alkio alkio alkio alkio Baltimore Rooma Seattle Toronto Kuva 36 Yhteen suuntaan linkitetty lista Viittaukset varsinaisiin alkioihin on esitetty alas suunnatuin (sinisin) nuolin ja viittaukset seuraaviin oikealle suunnatuin nuolin Tyhjä olio null on kuvattu symbolilla 3 kappale 92 Listan alkua ja loppua voidaan kutsua myös pääksi (head) ja hännäksi (tail) Jälkimmäisestä on viittaus tyhjä-arvoon (null) Tässä listassa on linkitys yksinkertaisimmillaan, so yhteen suuntaan Taulukon tapaan alkiot ovat listassa lineaarisessa järjestyksessä, joka sallii ketjutuksen alkioiden eli solmujen välillä Lista ei kuitenkaan edellytä kiinteää kokoa Alkio vaatii tilaa O(1) tietoa ja viittausta seuraavaan varten Kaikkiaan tilaa tarvitaan O(n), kun solmuja on n Määritellään luokka Node oheisena, joka esittelee listan solmujen oliot Lisäksi pitäisi antaa luokka viittauksen ylläpitämiseksi listan alkuun 3 kappale 93

class Node { private Object element ; // element stored in this node private Node next; // reference to the next node in the list // constructors public Node() { // create a node with a null element and next reference this(null, null); public Node(Object e, Node n) { // create a node given element // and next element = e; next = n; Koodi 39 Yhteen suuntaan linkitetyn listan solmun toteutus // update methods public void setelement(object newelem) { element = newelem; public void setnext(node newnext) { next = newnext; // accessor methods public Object getelement() { return element; public Node getnext() {return next; Koodi 39 (jatkoa) Yhteen suuntaan linkitetyn listan solmun toteutus Lisäys tai poisto on helposti tehtävissä listan alkuun eli päähän ajassa O(1), kuten kuvassa 37 3 kappale 94 3 kappale 95 pää pää seuraaja seuraaja seuraaja alkio alkio alkio pää Rooma (a) Seattle Toronto Baltimore Rooma Seattle (c) Toronto Baltimore Rooma Seattle (b) Toronto Kuva 37 Alkion lisäys listaan: (a) ennen lisäystä, (b) uuden solmun luominen 3 kappale 96 Kuva 37 (jatkoa) (c) Lisäyksen jälkeen Alkion poistaminen listasta on lisäykselle symmetrinen toiminta, jota voidaan kuvata käänteisessä järjestyksessä: (c), (b) ja (a) Lisäys voidaan suorittaa myös listan loppuun eli häntään niin ikään ajassa O(1), kuten kuvassa 38 3 kappale 97

pää häntä pää häntä pää Rooma (a) Seattle häntä Toronto Rooma Seattle Toronto Zürich (c) Kuva 38 (jatkoa) (c) Lisäyksen jälkeen Rooma Seattle Toronto Zürich (b) Kuva 38 Alkion lisäys listan loppuun: (a) ennen lisäystä, (b) uuden solmun luonti 3 kappale 98 Poistoa ei voida tehdä listan loppuun vakioajassa Vaikka on olemassa viittaus listan viimeiseen solmuun, on päästävä myös sitä edeltävään solmuun käsiksi viimeisen poistamiseksi, so ketjutuksen katkaisemiseksi viimeisen ja toiseksi viimeisen väliltä 3 kappale 99 Viimeisen solmun poistamista varten on käytävä lista alusta loppuun, jotta päästään viimeistä edelliseen solmuun käsiksi Tämä vaatii ajan Θ(n) Pinon toteutus yhteen suuntaan linkitetyn listan avulla Koska poiston suorittaminen listan lopusta ei ole vakioajassa mahdollista, on järkevää sijoittaa pinon huippu listan alkuun, jossa sekä lisäys että poisto ovat tehtävissä ajassa O(1), kuten kaikki muutkin operaatiot, myös size, kun talletettujen alkioiden määrästä ylläpidetään tietoa Tilaa tietorakenne vaatii O(n) Pinottaessa uusi alkio e luodaan sille uusi solmu n ja lisätään tämä listan alkuun Java-toteutus on oheisena (koodi 310) public class LinkedStack implements Stack { private Node top; // reference to the top node private int size; // number of elements in the stack public LinkedStack() { // Initialize the stack top = null; size = 0; public int size() { // Returns the current stack size return size; public boolean isempty() { // Returns true iff the stack is empty if (top == null) return true; return false; 3 kappale 100 Koodi 310 Pinon toteuttaminen listan avulla 3 kappale 101

public void push(object obj) { // Push a new object on the stack Node n = new Node(); nsetelement(obj); nsetnext(top); top = n; size++; public Object top() // Return the top stack element throws StackEmptyException { if (isempty()) throw new StackEmptyException("Stack is empty"); return topgetelement(); Koodi 310 (jatkoa) Pinon toteuttaminen listan avulla public Object pop() // Pop off the top stack element throws StackEmptyException { Object temp; if (isempty()) throw new StackEmptyException("Stack is empty"); temp = topgetelement(); top = topgetnext(); // link-out the top node size--; return temp; Koodi 310 (jatkoa) Pinon toteuttaminen listan avulla 3 kappale 102 3 kappale 103 Jonon toteutus käyttäen yhteen suuntaan linkitettyä listaa Jono voidaan toteuttaa tehokkaasti listana, kun alkioiden poisto jonon alusta suoritetaan nimenomaan listan alusta eli päästä ja lisäys jonon loppuun tehdään nimenomaan listan loppuun eli häntään Tilanteen ollessa päinvastainen toteutus olisi tehoton Mieti, miksi on näin! Kaikki jonon listatoteutuksen metodit toimivat ajassa O(1) Metodit ovat hieman pinon tapausta mutkikkaampia, koska erikoistapauksina ovat tilanteet, joissa jono on tyhjä ennen lisäystä ja jono tyhjenee poiston jälkeen Abstrakti tietotyyppi on esitetty oheisena, koodi 311 public void enqueue(object obj) { // Place a new object at the rear of the queue Node node = new Node(); nodesetelement(obj); nodesetnext(null); // node will be new tail node if (size == 0) head = node; // special case of a previously empty queue else tailsetnext(node); // add node at the tail of the list tail = node; // update the reference to the tail node size++; Koodi 311 Metodi enqueue lisäystä varten yhteen suuntaan linkitetyssä listassa jonon abstraktina tietotyyppinä 3 kappale 104 3 kappale 105

public Object dequeue() throws QueueEmptyException { // Remove the first object from the queue Object obj; if (size == 0) throw new QueueEmptyException("Queue is empty"); obj = headgetelement(); head = headgetnext(); size--; if (size == 0) tail = null; // the queue is now empty return obj; Koodi 311 (jatkoa) Metodi dequeue poistoa varten yhteen suuntaan linkitetyssä listassa jonon abstraktina tietotyyppinä 34 Kaksiloppuiset jonot Käsitellään jonomaista tietorakennetta, jossa lisäys ja poisto voidaan tehdä sekä alkuun että loppuun Tällaista jonon laajennusta kutsutaan kaksiloppuiseksi jonoksi (doubleended queue tai deque (ei siis dequeue)) Kaksiloppuisen jonon abstrakti tietotyyppi Tämä on monipuolisempi kuin pinon tai jonon vastaavat Seuraavissa perusmetodeissa D viittaa kaksiloppuiseen jonoon 3 kappale 106 3 kappale 107 insertfirst(e): Lisää uuden alkion e jonon D alkuun Syöte: olio insertlast(e): Lisää uuden alkion e jonon D loppuun Syöte: olio removefirst(): Poistaa ja palauttaa jonon D ensimmäisen alkion Tulos: olio removelast(): Poistaa ja palauttaa jonon D viimeisen alkion Tulos: olio Myös muita metodeita on käytettävissä first(): last(): size(): isempty(): Palauttaa jonon D ensimmäisen alkion Tulos: olio Palauttaa jonon D viimeisen alkion Tulos: olio Palauttaa jonon D alkioiden määrän Tulos: kokonaisluku Tutkii, onko D tyhjä Tulos: totuusarvo 3 kappale 108 3 kappale 109

Esim 35 Sarja operaatioita ja niiden vaikutus alunperin tyhjään kaksiloppuiseen kokonaislukujonoon D operaatio tulos D insertfirst(3) - (3) insertfirst(5) - (5,3) removefirst() 5 (3) insertlast(7) - (3,7) removefirst() 3 (7) removelast() 7 () removefirst() virhe () isempty() tosi () insertfirst(9) - (9) insertlast(7) - (9,7) size() 2 (9,7) insetfirst(3) - (3,9,7) insertlast(5) - (3,9,7,5) removelast() 5 (3,9,7) Pinojen ja jonojen toteutus kaksiloppuisilla jonoilla Seuraavat yksinkertaiset vastaavuudet ovat voimassa pinon ja kaksiloppuisen jonon välillä 3 kappale 110 3 kappale 111 pinon metodi kaksiloppuisen jonon toteutus jonon metodi kaksiloppuisen jonon toteutus size() isempty() top() push(e) pop() size() isempty() last() insertlast(e) removelast() size() isempty() front() enqueue(e) dequeue() size() isempty() first() insertlast(e) removefirst() Vastaavasti jonon ja kaksiloppuisen jonon välillä ovat seuraavat vastaavuudet Kaksiloppuista jonoa käyttäen esitetään pinon rajapinta Tämän luokan metodit käsittävät kaksiloppuisen jonon metodien suoraviivaisia kutsuja (koodi 312) Mukana on virheidentarkastelu yritettäessä saada tai poistaa tyhjästä jonosta 3 kappale 112 3 kappale 113

public class DequeStack implements Stack { private Deque D; // holds the elements of the stack public DequeStack() { // Initialize the stack D = new MyDeque(); public int size() { // Return the number of elements of the stack return Dsize(); public boolean isempty() { // Return true iff the stack has no elements return DisEmpty(); public void push(object obj) { // Insert an element into the stack DinsertLast(obj); Koodi 312 Pinon rajapinnan toteutus kaksiloppuisen jonon avulla public Object top() // Access the top element of the stack throws StackEmptyException { // thrown if the stack is empty try { return Dlast(); // since the deque throws its own exception, we catch it // and throw the StackEmptyException catch (DequeEmptyException ece) { throw new StackEmptyException("Stack is empty!"); Koodi 312 (jatkoa) Pinon rajapinnan toteutus kaksiloppuisen jonon avulla 3 kappale 114 3 kappale 115 Kaksiloppuisten jonojen toteutus kaksoislinkitetyillä listoilla public Object pop() // Remove and return the top element of the stack throws StackEmptyException { // thrown if the stack is empty try { return DremoveLast(); catch (DequeEmptyException ece) { throw new StackEmptyException("Stack is empty!"); Koodi 312 (jatkoa) Pinon rajapinnan toteutus kaksiloppuisen jonon avulla Käytettäessä yhteen suuntaan linkitettyä listaa eo tapaan ei voitu listan lopusta poistaa alkiota ajassa O(1) Listarakennetta voidaan kuitenkin tehostaa lisäämällä siihen toinen linkitys, jolloin myös kyseinen poisto toimii vakioajassa Kaksoislinkitetyssä listassa on seuraava-linkin (next) lisäksi linkki edeltävään (prev) solmuun Toteutuksen yksinkertaistamiseksi on kätevää asettaa listan alkuun erillinen alkusolmu (header) ja loppuun loppusolmu (trailer) Nämä vale- tai vartijasolmut eivät sisällä listan alkioita, vaan ovat ainoastaan listan alun ja lopun merkkeinä Alkusolmun viittaus edeltävään ja vastaavasti loppusolmun viittaus seuraajaan ovat luonnollisesti tyhjiä (null) (kuva 39) 3 kappale 116 3 kappale 117

alku loppu alku loppu alku New York Providence Philadelphia (a) loppu alku Baltimore Baltimore New York Providence Philadelphia (c) loppu New York Providence Philadelphia (d) New York Providence Philadelphia alku loppu Baltimore (b) Kuva 39 Kaksoislinkitetyn listan päivitys käytettäessä erillisiä alku- ja loppusolmuja: (a) ennen lisäystä ja (b) uuden solmun luonti 3 kappale 118 Baltimore New York Providence (e) Kuva 39 (jatkoa) Kaksoislinkitetyn listan päivitys käytettäessä erillisiä alku- ja loppusolmuja: (c) lisäyksen jälkeen ja ennen viimeisen alkion poistoa, (d) viimeisen alkion poisto sekä (e) poiston jälkeen 3 kappale 119 Kaksoislinkitetyn listan sekä alkuun että loppuun on lisäys tehtävissä ajassa O(1) Täten kaikki kaksiloppuisen listan metodit voidaan toteuttaa kaksoislinkitetyn listan avulla ajassa O(1) Tilavaatimus on luonnollisesti O(n) Hieman enemmän tietorakenne vaatii tilaa täsmällisesti laskien kuin edelliset listat, koska käytettiin kahta linkitystä yhden sijasta ja lisänä vielä alkuja loppusolmua 3 kappale 120