F3: OU2, undantag, avbildningar, listor och träd. Carl Nettelblad

Relevanta dokument
F4: Mer om OU1, OU2, listor och träd. Carl Nettelblad

F3: Recursive descent, tokenisering, avbildningar och undantag. Carl Nettelblad

F5: Debriefing OU2, repetition av listor, träd och hashtabeller. Carl Nettelblad

F2: Algoritmanalys, sökning, sortering, recursive descent Carl Nettelblad

if (n==null) { return null; } else { return new Node(n.data, copy(n.next));

Klassen BST som definierar binära sökträd med tal som nycklar och enda data. Varje nyckel är unik dvs förekommer endast en

Grafik, grafiska användargränssnitt och rörliga bilder

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

public boolean containskey(string key) { return search(key, head)!= null; }

Observera. Tentamen Programmeringsteknik II Skrivtid:

Tentamen Programmeringsteknik II Inledning. Anmälningskod:

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din kod överst i högra hörnet på alla papper.

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren att du har förstått dessa även om detaljer kan vara felaktiga.

Länkade strukturer, parametriserade typer och undantag

Tentamen Programmeringsteknik 2 och MN Skrivtid: Inga hjälpmedel.

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din kod överst i högra hörnet på alla papper.

Anmälningskod: Lägg uppgifterna i ordning. Skriv uppgiftsnummer (gäller B-delen) och din kod överst i högra hörnet på alla papper

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din kod överst i högra hörnet på alla papper.

Obligatorisk uppgift 5

Föreläsning 4 Innehåll. Abstrakta datatypen lista. Implementering av listor. Abstrakt datatypen lista. Abstrakt datatyp

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din kod överst i högra hörnet på alla papper.

Obligatorisk uppgift: Numerisk kalkylator

Obligatorisk uppgift: Numerisk kalkylator

Föreläsning 4 Innehåll

Tommy Färnqvist, IDA, Linköpings universitet

Abstrakta datatyper. Primitiva vektorer. Deklarera en vektor

Det är principer och idéer som är viktiga. Skriv så att du övertygar rättaren om att du har förstått dessa även om detaljer kan vara felaktiga.

Tentamen Programmeringsteknik II och NV2 (alla varianter) Skriv bara på framsidan av varje papper.

Tentamen Programmeringsteknik II Skrivtid: Hjälpmedel: Java-bok (vilken som helst) Skriv läsligt! Använd inte rödpenna!

Tentamen Programmeringsteknik II Skrivtid: Hjälpmedel: Java-bok (vilken som helst) Skriv läsligt! Använd inte rödpenna!

Föreläsning Datastrukturer (DAT036)

List.java. List.java. Printed by Tom Smedsaas

Parsing med Recursive Descent, Avbildningsklasser. Syntaxdiagram. Syntaxdiagram och kodning expression. Betrakta följande uttryck

F7: Rekursiv till iterativ, sammanfattning, genomgång av omtentan Carl Nettelblad

Föreläsning 3. Stack

Listor. Koffman & Wolfgang kapitel 2, avsnitt , och 2.9

Obligatorisk uppgift: Numerisk kalkylator

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din anmälningskod överst i högra hörnet på alla papper.

Inlämningsuppgift MiniPlotter

Tentamen Programmeringsteknik II för STS Skrivtid: Inga hjälpmedel.

Tentamen, Algoritmer och datastrukturer

Programmering för språkteknologer II, HT2014. Rum

Föreläsning 9 Innehåll

Föreläsning 9 Innehåll

Föreläsning 2. Länkad lista och iterator

Algoritmanalys. Inledning. Informationsteknologi Malin Källén, Tom Smedsaas 1 september 2016

Föreläsning 7. Träd och binära sökträd

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Föreläsning 10 Datastrukturer (DAT037)

Föreläsning 7 Innehåll. Rekursion. Rekursiv problemlösning. Rekursiv problemlösning Mönster för rekursiv algoritm. Rekursion. Rekursivt tänkande:

Repetition av OOP- och Javabegrepp

Föreläsning 3. Stack

Idag. Exempel, version 2. Exempel, version 3. Ett lite större exempel

Inlämningsuppgiften. Föreläsning 9 Innehåll. Träd. Datastrukturer i kursen

Datastrukturer i kursen. Föreläsning 8 Innehåll. Träd rekursiv definition. Träd

Föreläsning 7. Träd och binära sökträd

Repetition av OOP- och Javabegrepp

Lite om felhantering och Exceptions Mer om variabler och parametrar Fält (eng array) och klassen ArrayList.

TENTAMEN: Algoritmer och datastrukturer. Läs detta! Uppgifterna är inte avsiktligt ordnade efter svårighetsgrad.

Objektorienterad programmering i Java Undantag Sven-Olof Nyström Uppsala Universitet Skansholm: Kapitel 11

Föreläsning 9 Datastrukturer (DAT037)

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 3 Jonas Lindgren, Institutionen för Datavetenskap, LiU

Föreläsning 2 Datastrukturer (DAT037)

Lägg uppgifterna i ordning. Skriv uppgiftsnummer och din anmälningskod överst i högra hörnet på alla papper.

Tentamen Programmeringsteknik II

Föreläsning 3 Datastrukturer (DAT037)

Tentamen Datastrukturer (DAT036)

Datastrukturer, algoritmer och programkonstruktion (DVA104, HT 2014) Föreläsning 5

Tentamen i Algoritmer & Datastrukturer i Java

Lösningsförslag till exempeltenta 1

Föreläsning 4 Datastrukturer (DAT037)

Trädstrukturer och grafer

Föreläsning 13. Träd

Föreläsning 3 Datastrukturer (DAT037)

Tentamen, EDA690 Algoritmer och Datastrukturer, Helsingborg

Tentamen Datastrukturer, DAT037 (DAT036)

Föreläsning 2. Länkad lista och iterator

Dugga Datastrukturer (DAT036)

EDAA20 Föreläsning Klassen ArrayList. Viktiga operationer på ArrayList. Generisk klass

OOP Objekt-orienterad programmering

Föreläsning Datastrukturer (DAT036)

Föreläsning 3 Innehåll. Generiska klasser. Icke-generisk lista ArrayList, skiss av implementering. Icke-generisk lista Risk för fel

Tentamen'('Datastrukturer,'algoritmer'och'programkonstruktion.'

Dagens text. Programmeringsteknik. Mer om Scanner-klassen. Dialogrutor (klassen JOptionPane) Bubbelsortering. Omslagsklasser.

Objektorienterad programmering i Java Undantag Sven-Olof Nyström Uppsala Universitet Skansholm: Kapitel 11

Datastrukturer. föreläsning 3. Stacks 1

Föreläsning 8 - del 2: Objektorienterad programmering - avancerat

Sätt att skriva ut binärträd

Felhantering TDDD78, TDDE30, 729A

Tentamen i Algoritmer & Datastrukturer i Java

Grundkurs i programmering, 6 hp (725G61) Dugga 2 tillfälle 2

Abstrakt datatyp. -Algoritmer och Datastrukturer- För utveckling av verksamhet, produkter och livskvalitet.

TDDE10 m.fl. Objektorienterad programmering i Java Föreläsning 6 Erik Nilsson, Institutionen för Datavetenskap, LiU

Kungl. Tekn. Högskolan Förel 1, bild 1 Föreläsning 1: Introduktion ffl Kursinnehåll ffl Javarepetition ffl Referenser ffl Nyckelordet static ffl Klass

Del A (obligatorisk för alla)

TDDC30. Objektorienterad programmering i Java, datastrukturer och algoritmer. Föreläsning 8 Erik Nilsson, Institutionen för Datavetenskap, LiU

Dagens text. Programmeringsteknik. Mer om Scanner-klassen. Dialogrutor (klassen JOptionPane) Bubbelsortering. Omslagsklasser.

F12 - Collections. ID1004 Objektorienterad programmering Fredrik Kilander

Linjärt minne. Sammanhängande minne är ej flexibelt. Effektivt

Transkript:

F3: OU2, undantag, avbildningar, listor och träd Carl Nettelblad 2019-04-08

Säg vad du vill göra Varför skriver vi kod? För att få datorn att göra det vi vill För att själva läsa koden För att andra ska läsa koden Varför läser vi kod? För att rätta fel För att förbättra/förändra den för nya behov För att använda den (anropa den) på rätt sätt

VAR TYDLIG! Mycket hellre än var smart

Kodstil Glöm inte bra Javadoc Ge rimliga namn Se till att koden är rätt indenterad Editorn kan hjälpa dig Undvik Kopierad kod Mycket långa rader

Typiska idiom Kolla att stacken antingen är tom, eller att den inte innehåller ett element som är lägre än det som ska läggas till och lägg i så fall till elementet, kasta annars ett undantag Lite rörigt?

Bättre? Kolla att stacken, om den inte är tom, inte innehåller ett element som är lägre än det som ska läggas till Kasta i så fall ett undantag Lägg till elementet Även om villkoret i punkt 1 är lite rörigt är det tydligt att vi räknar med att punkt 2 utförs

I kod if (stack.isempty() x < stack.get(stack.size()-1)) { stack.add(x); else { throw new RuntimeException("Tried to push larger element");

I kod 2 if (!stack.isempty() && x > stack.get(stack.size()-1)) { throw new RuntimeException("Tried to push larger element"); else { stack.add(x);

I kod 3 if (!stack.isempty() && x > stack.get(stack.size()-1)) { throw new RuntimeException("Tried to push larger element"); stack.add(x); Normalfallet är inte gömt i en if-sats Alla håller inte med, en del tycker att det är viktigare att en metod alltid avslutas på samma ställe vi kommer tillbaka till det

Varje anrop är ett anrop Matematiskt kan vi definiera 2 n = f n som 1 n = 0 f n = ቊ f n 1 + f(n 1) n > 0

pow2 public long pow2_a(int n) { if (n <= 0) return 1; return pow2_a(n 1) + pow2_a(n 1); public long pow2_b(int n) { if (n <= 0) return 1; return 2 * pow2_b(n 1);

Varje anrop är ett anrop public int largest(int[] a, int i) { if (i == 0) return a[i]; if (a[i] > largest(a, i - 1)) return a[i]; else return largest(a, i 1);

Varje anrop är ett anrop public int largest(int[] a, int i) { if (i == 0) return a[i]; int largeval = largest(a, i - 1); if (a[i] > largeval) return a[i]; else return largeval;

Lägga till i en stack Uppgiftstexten talade om Se till att tiden för metoderna push och pop är oberoende av stackens storlek! Vad kan gå fel? Spelar det roll hur man lägger till elementet?

Algoritmanalys rekursion Hur lång tid en rekursion tar beror på Hur svårt det är att dela upp problemet Antalet delproblem Delproblemens storlek Arbetet för att kombinera dem Eftersom det är rekursivt sker detta på varje nivå

Några exempel Vi löser problem av storlek n, varje problem delas upp i D delproblem av storlek S, arbetet att dela upp och foga samman blir F. Ger total tid enligt nedan D S F Total tid Exempel 1 n-1 O(1) Θ(n) Linjär sökning (largest) 1 n-1 Θ(n) Θ(n 2 ) Enkla sorteringsmetoder 1 n/2 O(1) Θ(log n) Effektiv rekursiv beräkning x n, binär sökning 2 n-1 O(1) Θ(2 n ) Brickleken 2 n/2 Θ(n) Θ(n logn) Mergesort

Omöjliga problem Kortaste resvägen för att besöka alla städerna TSP, travelling salesman problem Ett lösningssätt: prova alla rekursivt Håll reda på vilka som redan är besökta Basfall: alla besökta Fördjupning: testa alla obesökta som nästa Sammanfogning: hitta minsta

Hur lång körtid? Antalet rekursionssteg blir att lista alla ordningar på listan med städer För n städer blir det n! Växer ungefär exponentiellt D = n 1, F = n 1, S = n 1 för att använda samma notation som innan

Hur lång körtid? Men visst måste det gå snabbare än att prova alla? Ofta Naturliga kartor har inte maskhål Men det går att bevisa att detta och andra problem är NP-svåra Det finns ingen känd lösning som hittar bästa vägen för en godtycklig version av problemet i polynomisk tid Väldigt annorlunda än Fibonacci Öppen fråga om NP-problemen har en P-lösning Ingen känd lösning, men inget bevis för att det inte går Många lutar åt att NP-problemen inte kan lösas i P (ungefär:) Körtid växer exponentiellt med problemstorlek

Hur lång körtid? Bästa rutten för en budfirma 20 paket till olika platser Om vi kan testa en ordning i vår sökning på 1 ns 1 miljard per sekund 20! är ungefär 2,5 triljoner Ungefär 77 år för att hitta lösningen Annat viktigt problem som tar lång tid Faktorisera tal med stora primfaktorer

Räkna millisekunder Räkna om millisekunder till genomsnittsår double time = c * pow(1.618, 100) / (1000 * 3600 * 24 * 365); Varför är detta fel? (Förutom att vi struntar i skottår.) double time = c * pow(1.618, 100) / (1000. * 3600 * 24 * 365);

OU2 igen Recursive descent Varje element i syntaxdiagram motsvaras av en metod Metoderna läser från strömmen från vänster till höger Varje uttryck ett antal termer förenade med + -, som i sin tur består av faktorer förenade med * / Fel ska rapporteras Värden på variabler ska hanteras mellan olika uttryck Lagras i ett uttryck, användas i samma eller senare

Beskriva syntax BNF (Backus-Naur-form) expression ::= term [( + / - ) term]* Syntaxdiagram

Beskriva syntax Pseudokod ( nästan Java) double expression() double sum = term() while ( nästa tecken + eller - ) Läs förbi tecknet if ( det var ett + ) else sum = sum + term() sum = sum - term() Syntaxdiagram return sum Ett uttryck är en sekvens av en eller flera termer, åtskilda med + eller

Recursive descent Skriv regler från låg till hög prioritet Anropa inre regler En högprioritetsregel slutar tolka när uttrycket inte matchar Vanlig typ av syntaxfel att missa att läsa ända till strömslut a * 3 ^ 4 kommer att ge denna typ av fel i vår kalkylator Det fel som rapporteras är expected EOL, men det verkliga felet är ^ not recognized

Felhantering Olika språk och olika problem har olika filosofi om felhantering Är det viktigaste att programmet fortsätter köra? Kanske en bra idé att bara köra vidare, även om fel inträffar Inte säkert att det går/ger något rimligt resultat Är det viktigaste att programmet ger rätt svar? Se till att fel alltid rapporteras/hanteras Hantering kan innebära låt programmet krascha

Hur kan vi hitta ett fel? Metoder kan returnera när något går fel if (x.move()!= STATUS_OK) { return false; if (x.eat()!= STATUS_OK) { return false; if (x.sleep()!= STATUS_OK) { return false; Om vi glömmer att kolla feltillstånd, eller gör det på fel sätt, kan det vara väldigt svårt att hitta grundorsaken Det här sättet att hantera fel är vanligt i lågnivåspråk, som C Många farliga säkerhetsbuggar kommer av felaktig felhantering

Varför blir detta svårt? Flera lager av metoder kan anropa varandra Om kalkylatorn är rekursiv i flera led och använder Stokenizer, som använder Java-biblioteken för att läsa från en fil som finns på en USB-sticka som du tog ur datorn Vilken del av koden vet vad som är rätt sak att göra? Felet måste bubbla upp i flera lager av anrop Varje metod skulle behöva kunna returnera en beskrivning av vilka fel som inträffade Bökigt, ändå risk att någon glömmer

Javas lösning - Undantag En metod i Java kommunicerar med anroparen genom returvärdet och förändringar av de objekt som skickades in och genom att potentiellt kasta ett undantag (Exception) Eller mer exakt en Throwable (basklass till Exception) Vissa typer av drastiska fel som OutOfMemoryError och StackOverFlowError är Throwable, men inte Exception

Vad är då ett undantag? En instans av en klass Egentligen som vilken klass som helst Ärver (direkt/indirekt) från java.lang.exception Man talar om en subklass till Exception Klassens typ och innehåll beskriver ett fel som har inträffat Arv är mycket vanligt i objektorienterad programmering Det viktigaste är att en instans av en subklass kan användas överallt där basklassen används Varje FileNotFoundException är ett Exception. Men varje Exception är inte ett FileNotFoundException.

Klasshierarki java.lang.object java.lang.throwable java.lang.exception java.io.ioexception» java.io.filenotfoundexception java.lang.runtimeexception java.lang.error java.lang.virtualmachineerror» Java.lang.StackOverFlowError Naturligtvis många fler Mycket vanligt att skapa egna undantagsklasser

Inte vilken klass som helst Det är bra att vi har ett sätt att kategorisera fel Java har inbyggt stöd i språket för att hantera undantag Undantag kan kastas När ett fel inträffar Metoden som kastar slutar köras Undantag propageras Från den metod som kastar, till den metod som anropade den, till den metod som anropade den

Fånga undantag Undantag kan fångas Den metod i anropskedjan som begriper sig på felet kan fånga det Slutar propagera Ibland vill man återkasta (vi kunde inte hantera hela felet, men vi städar upp lite på vägen) Då propagerar det vidare Ett undantag som inte fångas någonstans slutar i att hela programmet slutar köras och att ett fel skrivs ut Det har vi sett många gånger

Vad gör man när man fångar? Olika svar kan vara rätt Ovanligt: Fånga utan att göra någonting Ger fel som är svåra att felsöka Vanligt: Skriva ut (på skärmen eller i loggfil) att ett fel inträffade Försöka igen Använda någon typ av reservlösning För kalkylatorn: Rapportera felet och låt användaren göra om

Tillbaka lite till SpecialStack Vi har redan kastat RuntimeException throw new RuntimeException("Stack is empty."); throw är det nya här, resten är helt vanlig Javakod som vi redan sett Skapa ett nytt objekt, anropa konstruktorn som tar en sträng Många undantagsklasser har konstruktorer som tar emot en sträng som fungerar som felmeddelande

Hur borde vi ha gjort? Egen undantagsklass public class SpecialStackException extends RuntimeException { public SpecialStackException(String msg) { super(msg); // anropar basklassens konstruktor Inte mer än så! Bara en konstruktor som tar felmeddelandet.

Undantag i kalkylatorn I OU2 ska vi ha två egna undantagsklasser: public class SyntaxException extends RuntimeException { public SyntaxException(String msg) { super(msg); public class EvaluationException extends RuntimeException { public EvaluationException(String msg) { super(msg);

RuntimeException eller Exception Java skiljer egentligen på RuntimeException och Exception RuntimeException kan kastas var som helst Andra subklasser till Exception får bara kastas om de fångas eller om metoden anger throws i metodhuvudet Om SyntaxException ärvde från Exception skulle exempelvis metoden term behöva se ut något så här double term(stokenizer t) throws SyntaxException Vi skippar detta och väljer därför att ärva från RuntimeException

Syntaxdiagram statement Tom rad Specialkommandon quit, vars, file Ett (tilldelnings)uttryck

Syntaxdiagram assignment Ett uttryck Potentiellt följt av ett likhetstecken Syntaxfel om det inte kommer ett ord efter likhetstecknet 5 = 5 funkar inte 5 = 3 funkar inte heller 5 = (x) funkar inte heller

Syntaxdiagram expression En term Potentiellt följt av + eller och fler termer + och kan blandas 5 + 3 2 x + 7

Syntaxdiagram term En faktor Potentiellt följt av * eller / och fler faktorer * och / kan blandas x * 3 / 2 / (y + 5) * sin(3)

Syntaxdiagram factor Ett primärt element En uppdelning som vi inte hade på föreläsningen förra veckan Förbereder för OU5

Syntaxdiagram primary Parenteser (med assignment inuti) Funktionsanrop (med assignment som parameter) Ett tal Unärt minus -x, -sin(5), -(3), -4 Variabel Syntaxfel Högerparentes saknas efter assignment (5 + 3 Ingen parentes efter funktionsnamn Falla ut helt och hållet 5 + sin x Texten efter + ska tolkas som en term, som blir en factor, som blir en primary

Term Pseudokod double term() double prod = factor() while ( nästa tecken * eller / ) Läs förbi tecknet if ( det var ett * ) else prod = prod*factor() prod = prod/factor() Syntaxdiagram return prod En term är en sekvens av en eller flera faktorer, åtskilda med * eller /

Factor Pseudokod double factor() if ( nästa tecken ( ) Läs förbi tecknet result = expression() Syntaxdiagram if ( nästa tecken inte ) ) FEL! return result else return number() En faktor är ett tal eller ett uttryck omgivet av parenteser

Exempel i kod if (tokenizer.getchar()!= ) ) { throw new SyntaxException("Expected ) but found " + tokenizer.gettoken()); Detta propageras tills det fångas

Fånga i kod Om ett uttryck är ogiltigt är det koden som bad om att parsa uttrycket som kan hantera felet I en tänkt main-metod i lösningen while (true) { try { System.out.print("? "); double value = expression(); System.out.println("Value: " + value); catch (SyntaxException se) { System.out.println("Syntax error: " + se.getmessage(); // Skip to end of line... Koden under catch körs bara om ett SyntaxException inträffar. Om inget undantag inträffar, körs den inte. Om ett annat undantag inträffar, fortsätter det att propageras. Varför är det viktigt att ordna Skip to end of line?

Fånga undantag Varför skriver vi inte catch (Exception e) i stället? Då hanterar vi alla fel. Men vi vet inte vad felet egentligen är. Är åtgärden att läsa till slutet av raden och sedan frågan efter nästa uttryck rätt i alla situationer?

Undantag eller assert Undantag är ett sätt att hantera fel Fel som det går att undvika, som antas kunna hända i normal drift assert är ett sätt att uttrycka saker som alltid ska gälla Kan användas av automatiska verktyg för att analysera koden Om man kör koden med vissa inställningar kastas ett Error (inte Exception) Inte meningen att det ska fångas eller något man ska lita på, bara ett sätt att identifiera misstag under utveckling Skillnad på fel under körning och logiska fel/misstag i koden

Avbildningar - Map Vi har använt arrayer och listor (ArrayList) Java har inbyggt stöd för fler datastrukturer Avbildningar bland de mest användbara Nycklar av en datatyp Högst ett värde per nyckel i avbildningen Samma värde kan förekomma för flera nycklar Vanligt att nycklarna är strängar Namn och tillhörande värden I kalkylatorn kan det vara variabelnamn och deras värden

I kod import java.util.treemap; import java.util.hashmap; HashMap<String, double> vars = new HashMap<String, double>(); vars.put("x", 15.0); vars.put("y", vars.get("x")); if (!vars.containskey("y")) { System.out.println("Strange, key just added does not exist.");

Försöka läsa nyckel som inte finns if (vars.get("z") == null) { System.out.println("Getting a key which does not exist yields null, not an Exception"); Fel som bara indikeras med nullvärde, stor risk att man får ett NullPointerException när det värde man plockade ut ska användas Svårt att se att det berodde på att det saknades i avbildningen Att spara det utplockade värdet i en double ger detta Värdet som lagras är ett objekt av klassen Double som sedan ska konverteras till primitiva typen double

Sammanfattning Kod ska vara tydlig Nästa uppgift: numerisk kalkylator Rekursiv parsning av tokens Undantag hjälper oss att skriva rak kod Utan att missa att hantera felen Utgå från kodskelettet Få in rapportering av fel i Calculator Implementera alla delar av syntaxdiagrammen, inklusive att fel kastas Se till att hela er kod har rimlig kodstil och att ni begriper den! Avbildningar är flexibla datastrukturer

Listor och träd Grafer Noder och kanter Listor och träd de enklaste Utgångspunkten för OU3

Linjär lista Varje element har en föregångare och efterföljare Listan som helhet har en början och ett slut Kö, stack

Arraybaserade listor Elementen har inte bara efterföljare och föregående, utan index Kan nå vilket element som helst snabbt, O(1) tid Lägga till/ta bort element dyrt Det gäller oavsett om du skriver det själv eller använder ArrayList Problem om man exempelvis vill hantera en kö Eller en lång deltagarlista med personer där personer kan komma och gå i olika ordning

Länkade listor I stället för index Låt varje element peka på sin efterföljare Fördel: Lätt att förändra listan. Om man har föregående element kan en ny efterföljare läggas till i tid O(1). Kan användas för att ta bort, lägga till, flytta element. Nackdel: Svårt att hitta element i viss position (O(n)). Långsammare att stega mellan element (O(1), men tar klart längre tid). Använd inte länkade listor om du inte specifikt vill åt fördelarna.

Klasser för länkade listor Precis som ArrayList finns LinkedList i Java Men nu ska vi inte använda den! Vi ska skriva vår egen klass Övning för att själv kunna skapa mer avanceade grafer och datastrukturer Övning i rekursivt tänkande En listan kan vara Tom Eller ett element, som i sig pekar på en lista

Exempel En klass List För länkade listor med heltal i listnoderna Listan sorterad (bra i vissa fall) Samma värde får bara förekomma en gång Annorlunda än i uppgiften Vår klass blir en representation av mängder med heltal Anmärkning: De flesta tekniker vi diskuterar är generella, gäller mer än bara heltal.

Enkellänkad lista Varje element pekar på sin efterföljare Inte föregångare! Hitta föregångare lika dyrt som att hitta visst index Varje element representeras med klassen Node Rekursiv definition, en Node har en referens till en (annan?) Node class Node { int data; Node next;

Vad är en lista? Node-instansen för första elementet definierar hela listan Normalt sett brukar man ändå ha en yttre ägarklass Metoder för att hantera listan Representera tomma listor

Klassen List public class List { private Node first; private Node last; // Optional

Klassen List public class List { private static class Node { int data; Node next; Inre klass. Vilken skillnad gör static? Node(int d, Node n) { data = d; next = n; private Node first; private Node last; // Optional...

tostring public String tostring() { return "[" + tostring(first) + "]"; private static String tostring(node n) { if (n==null) { return ""; else { return n.data + " " + tostring(n.next); Varför static? Vanligt mönster med hjälpmetod som vandrar rekursivt genom elementen Skulle kunna skrivas iterativt också, ibland svårare

Stoppa in nya element Hitta elementet före önskad position Detta element ska få ny efterföljare

Stoppa in nya element, iterativt public void additer(int x) { if (first == null x < first.data) { first = new Node(x, first); // New start of list else { Node n = first; while (n.next!=null && x > n.next.data) { n = n.next; if ( n.next==null x < n.next.data) { n.next = new Node(x, n.next);

Stoppa in nya element, rekursivt public void add(int k) { first = add(k, first); // Return new successor to previous node, // if current next node is n private static Node add(int k, Node n) { if (n==null k<n.data) { return new Node(k, n); else if (k>n.data) { n.next = add(k, n.next); return n;

Kopiera listor Vill ha en kopia av en hel lista Rimligen för att vi ska kunna redigera kopian Eller originalet Försök 1: public List copy() { List result = new List(); result.first = this.first; return result;

Kopiera lista public List copy() { List result = new List(); for (Node n=first; n!=null; n = n.next) { result.add(n.data); return result; Nytt försök Åtminstone korrekt resultat Men kanske inte idealiskt? (Tidsåtgång?)

Kopiera lista Rekursiv hjälpmetod för nästa efterföljare Fundera över: Hur skriva samma algoritm iterativt? public List copy() { return new List(copy(first)); // Private constructor private static Node copy(node n) { if (n==null) { return null; else { return new Node(n.data, copy(n.next));

Andra länkade listor Enkellänkad lista enkel En extra referens för varje element Och elementklassen Algoritmerna enkla Men svårt att gå bakåt från element, gå till slutet

Dubbellänkat Kan nå både efterföljare och föregångare till ett element i O(1) tid Mer minne, fler referenser att hålla reda på i algoritmerna Cirkulärt Nå både första och sista i O(1) tid Listan håller reda på sista, sistas efterföljare är första Algoritmer måste ändras för att inte gå runt oändligt

Dubbellänkad cirkulär Många operationer möjliga, men alla algoritmer blir mer komplicerade och lagringen kräver mer minne Enkellänkad kan ofta vara enklast och effektivast Ibland med tillägget att listobjektet också håller reda på direktpekare till sistaelementet, och/eller totala antalet element

LinkedList<E> i Java Dubbellänkad lista av element med typ E Implementerar samma metoder som ArrayList<E> Men inte samma tidegenskaper Några metoder: boolean add(e e) void addfirst(e e) void clear() boolean contains(object o) E get(int index) E getfirst() E getlast() E remove(int index) E removefirst() E removelast() E set(int index, E e) int size()

Träd Ett (rotat) träd är en mängd noder där En speciell nod kallas rot Övriga noder delas in i m disjunkta delmängder som i sin tur är rotade träd Rekursiv definition! Inga cykler Tydlig riktning på relation mellan noder

Lite terminologi

Binära träd Ett binärt träd är en mängd noder som antingen är tom eller som består av tre olika delmängder Exakt en nod: roten Vänster delträd Höger delträd Observera att vänster delträd kan vara tomt Höger delträd något annat än bara andra barnet

Operationer på träd Orientering, vandring mellan noderna Traversering, besök alla noder Sökning efter nodinnehåll eller position Inlägg/uttag av noder Sammanfogning av flera träd Mätning av höjd, bredd, antal noder, väglängd, balans

Traversering av träd Besök alla noder i ett (del)träd Naturligt rekursiv beskrivning Men vilken ordning ska vi gå i? Spelar det någon roll?

Preorder 1. Besök roten 2. Besök barnträden i preorder (äldsta först) Resultat: e b a d c n g f k p y

Postorder 1. Besök barnträden i postorder (äldsta först) 2. Besök roten Resultat: a c d b f k g y p n e

Inorder (binära träd) 1. Besök vänster delträd inorder 2. Besök roten 3. Besök höger delträd inorder Resultat: a b c d e f g k n p y

Representera binära träd i kod Precis som med listor skapar vi nodobjekt Som refererar till andra nodobjekt class Node { int data; Node parent; Node left; Node right;... Förälderpekaren är ofta onödig, jämför enkellänkad/dubbellänkad lista.

Exempel: BinarySearchTree En klass som lagrar textsträngar som data i ett binärt träd Trädet är ett binärt sökträd, med följande regel för alla noder Varje nyckel (unik sträng) finns alltså endast en gång Praktisk struktur för sökning, inläggning och urtag Men inte oproblematisk

Nodrepresentation private static class Node { private String value; private Node left, right; private Node(String value, Node left, Node right) { this.value = value; this.left = left; this.right = right;

Placering av klassen public class BinarySearchTree { private static class Node {... private Node root; public BinarySearchTree() { root = null;

tostring public String tostring() { return tostring(root); private static String tostring(node r) { if (r==null) { return ""; else { return tostring(r.left) + " " + r.value + " " + tostring(r.right); Vilken typ av traversering är detta? Preorder, postorder, inorder?

Lämna till hjälpmetod public void add(string value) { root = add(root, value); Lägga till element

Lägga till element private static Node add(node r, String value) { if (r==null) { return new Node(value,null,null); int compare = value.compareto(r.value); if (compare<0) { r.left = add(r.left, value); else if (compare>0) { r.right = add(r.right, value); return r; Fortsätt tills underträd saknas och lägg in ny nod där med värdet Låt jämförelse styra önskat värde Eller stanna om befintligt element har samma värde Jämför med likheterna i List

Egenskaper hos binära träd Ett binärt träd med k fyllda nivåer innehåller n noder n = 1 + 2 + 4 + + 2 k 1 = 2 k -1 k = log 2 (n + 1) log 2 n I ett sådant träd blir arbetet för Sökning O log n Inlägg O log n Uttag O log n

Sämsta möjliga träd I ett sådant träd blir arbetet för Sökning O log n Inlägg O log n Uttag O log n Trädet en linjär lista Algoritmerna som algoritmerna för enkellänkad

Vad händer för mitt träd? Vad gäller i genomsnitt? Vad är genomsnitt? I genomsnitt i ett genomsnittligt träd? Tre mått som påverkar trädets prestanda: Höjd ger rekursionsdjup i värsta möjliga fall Intern väglängd: för lyckad sökning Extern väglängd: för misslyckad sökning

Hjälpmetoder Publika metoder finns för att ge ett tydligt kontrakt till omvärlden Så här använder du den här klassen Hjälpmetoderna finns för att ge dig en tydlig struktur inne i klassen Kan göra saker som är förbjudna utifrån Kan ta andra parametrar och returnera andra värden än de publika metoderna Klassmetoder (static) om all information finns i parametrarna och returvärdet

Exempel Hjälpmetoden tar en parameter om vad den ska jobba på private static String tostring(node n) för public String tostring() Annan returtyp private static Node add(int k, Node n) för public void add(int k) private static Node copy(node n) för public List copy()

Hjälpmetoder med void som returtyp Hjälpmetoden kan ta in ett objekt som parameter och förändra det objektet Behöver inte returnera något för att kommunicera med omvärlden public ArrayList<Integer> toarraylist() { ArrayList<Integer> result = new ArrayList<Integer(); toarraylist(first, result); return result; private static void toarraylist(node n, ArrayList<Integer> al) { if (n!= null) { al.add(n.data); toarraylist(n.next, al);

Hjälpmetoder static gör det tydligt att instansen inte påverkas på andra sätt än genom parametrarna Hjälpmetod med samma namn som den publika metoden gör det tydligt vem man hjälper Men det är inte säkert att ovanstående gäller för alla hjälpmetoder Viktigaste är att undvika enskilda väldigt krångliga metoder, eller kopierad kod Välj vilka hjälpmetoder du har, med vilken signatur (static? parametrar? returtyp? namn?) för att ge maximal nytta och tydlighet