Tentamen Programmeringsteknik II 205-0-23 Inledning I bilagan finns ett antal mer eller mindre ofullständiga klasser. Några ingår i en hierarki: BasicList, List, SortedList och Queue enligt vidstående figur. Klassen BasicList innehåller några grundläggande komponenter för att skapa och hantera enkellänkade listor av heltal (för enkelhetens skull). Klassen List som ärver från BasicList innehåller ytterligare ett antal metoder. SortedList Klassen SortedList, som ärver från List, underhåller listor som är sorterade på datainnehållet. Klassen Queue ärver från BasicList och representerar köer dvs en struktur där man lägger till i ena änden och tar ut i andra änden. Vidare finns en klass BST som representerar binära sökträd. Förutom nyckeln (heltal) så finns också en räknare i noderna som håller reda på hur många gånger den nyckeln har adderats. Slutligen finns ett embryo till en klass som representerar släktträd med en urfader eller urmoder, hens barn, barnbarn,.... Queue BasicList List Del A (obligatorisk för alla) A. Skriv klart metoderna addfirst och clear i klassen BasicList. public void addfirst(int data) { first = new Node(data, first); public void clear() { first = null; A2. Skriv klart metoden size i klassen BasicList. Metoden skall returnera listans längd. Metoden får implementeras rekursivt eller iterativt. public int size() { return size(first); public int size(node n) { if (n == null) { return 0; else { return + size(n.next);
A3. Rita det träd som genereras av main-metoden i klassen BST. Strukturen på trädet ska framgå liksom värdet på både nyckel (key) och och räknare (count). 0 2 5 2 3 7 20 25 30 A4. Skriv klart metoden numberofitems() som returnerar totala antalet gånger som nycklar har adderats dvs summan av alla noders värden på count. public int numberofitems() { return numberofitems(root); public static int numberofitems(node r) { if (r==null) { return 0; else { return r.count + numberofitems(r.left) + numberofitems(r.right); 2
A5. Skriv klassen SortedList som ärver från List. Se den givna main-metoden som visar vad som behöver läggas till eller modifieras. public class SortedList extends List { public class SortedListException extends RuntimeException { public SortedListException(String msg) { super(msg); public void add(int data) { first = add(data, first); private static Node add(int data, Node f) { if (f == null data<f.data) { return new Node(data, f); else { f.next = add(data, f.next); return f; /******** Nonrecursive alternative: public void add(int data) { if (first==null data<first.data) { first = new Node(data, first); else { Node aheadof = first; while (aheadof.next!= null && data>aheadof.next.data) { aheadof = aheadof.next; aheadof.next = new Node(data, aheadof.next(; *********/ public void addfirst(int data) { if (first==null data<=first.data) { super.addfirst(data); else { throw new SortedListException("Illegal addfirst in SortedList: " + data + "!"); Ändra också i den givna main-metoden så att den inte avbryts när ett SortedListException inträffar. main-metoden ska fånga undantaget, skriva ut ett felmeddelande och sedan fortsätta. Så här ska det se ut: alist from testdata: [, 2, 3, 3, 3, 5, 7] addfirst(7) addfirst(3) addfirst(5) Illegal addfirst in SortedList: 5! Ignored. addfirst(3) addfirst(2) addfirst(3) Illegal addfirst in SortedList: 3! Ignored. addfirst() Result from addfirst test: [, 2, 3, 3, 7] 3
for (int data : testdata) { try { System.out.println("addFist(" + data +")"); alist.addfirst(data); catch(sortedlistexception e) { System.out.println(e.getMessage() + " Ignored."); System.out.println("Result from addfirst test: " + alist); 4
A6. Givet en metod som löser ett problem av storlek n med tidkomplexitet Θ(n 2 ). Mätning visar att ett problem av stoleken n = 000 tar sekund. Uppskatta hur lång tid det då tar för n = 0000. Motivera! Svar med enkel motivering: Eftersom tiden växer med kvadraten på antalet element tar ett 0 gånger större problem 00 gånger längre tid dvs 00 sekunder. Mer formellt: Att tiden är Θ(n 2 ) innebär att t(n) cn 2 för stora n. t(000) = c 000 2 = c = 0 6 t(0000) = c 0000 2 = 0 6 0000 2 = 00 A7. Vad är en lyssnare och i vilken typ av datorprogram används dessa och till vad? En lyssnare är en klass som innehåller metoder för att hantera olika typer av händelser. Tangenttryck och musklick är exempel på händelser. Även en timer kan skapa händelser. Används typiskt i händelsestyrda program. Del B (för betyg 4 och 5) B. Skriv klart iteratorn i klassen BasicList dvs skriv den inre klassen Iterator samt metoden Iterator iterator() så att de fungerar enligt körexemplet i main-metoden. public class Iterator { Node current; public Iterator() { current = first; public boolean hasnext() { return current!=null; public int next() { int result = current.data; current = current.next; return result; public Iterator iterator() { return new Iterator(); 5
B2. En kö är en struktur för lagring där den som kommer först till kön också kommer ut först. Skriv klassen Queue som en subklass till klassen BasicList. Metoden add ställer ett tal i kön och metoden remove tar ut och returnerar det som står på tur att lämna kön. Den givna main-metoden med körresultat visar hur det ska fungera. Tiden för både add och remove skall vara oberoende köns längd dvs de ska vara O()- operationer. Att försöka ta ut något från en tom kö liksom försök att ställa ett nytt element först i en icke-tom kö skall medföra att ett undantag av typen QueueException med ett passande meddelande kastas. Vi väljer att betrakta listans första element som det som står först i kön och listans sista element som det som står sist i kön. För att kunna lägga in sist oberoende av köns längd så måste vi även ha en referens till sista elementet. public class Queue extends BasicList { protected Node last; // new instance variable public class QueueException extends RuntimeException { public QueueException(String msg) { super(msg); public void add(int data) { Node n = new Node(data, null); if (first==null) { first = last = n; else { last = last.next = n; public int remove() { if (first==null) { throw new QueueException("Remove from empty queue"); else { int result = first.data; first = first.next; if (first == null) { // If the queue became empty last = null; return result; public void addfirst(int data) { throw new QueueException("Queue discipline???"); Vilka ytterligare saker hade man behövt göra om klassen Queue skulle ärva från List i stället för BasicList? Alla metoder List som ändrar i listan måste omdefinieras eftersom de kan behöva uppdatera last-pekaren. 6
B3. Skriv klart metoden mostcommon i klassen BST. Metoden ska returnerar en array med två element där det första är nyckeln i noden med högst count-värde och det andra är själva count-värdet. Som ofta är det är designen av hjälpmetoden nyckeln till att hitta en enkel, tydlig lösning. Vi låter hjälpmetoden returnera en referens med det högsta count-värdet. Då är det lätt för huvudmetoden att skapa den efterfrågade arrayen. public int[] mostcommon() { if (root == null) { throw new BSTException("Empty tree"); else { Node n = mostcommon(root); int[] returnvalue = new int[2]; returnvalue[0] = n.key; returnvalue[] = n.count; return returnvalue; private static Node mostcommon(node r) { if (r==null) return null; else { Node returnnode = r; Node lft = mostcommon(r.left); Node rgt = mostcommon(r.right); if (lft!= null && lft.count > returnnode.count) { returnnode = lft; if (rgt!= null && rgt.count > returnnode.count) { returnnode = rgt; return returnnode; B4. Antag att en hashtabell med länkade listor är dimensionerad till storlek 0. Vidstående figur illustrerar en sådan. Låt n beteckna totala antalet lagrade poster. Hur beror tiden att söka en given nyckel av n i genomsnitt? Svara med ett uttryck av typen Θ(), Θ(log n), Θ(n log n), Θ(n 2 ),... Motivera svaret! Du kan förutsätta att hashfunktionen är bra dvs att den sprider nycklarna någorlunda jämnt över tabellen. 0 2 3 4 9 Svar: Θ(n) Motivering: Eftersom tabellstorleken är fix så växer längden av listorna linjärt med antalet lagrade element även om konstanten i Θ-uttrycket är mindre än om elementen lagras i en enda linjär lista. 7
B5. Antag att det finns två algoritmer A och B för att lösa ett problem. Problemets storlek kan beskrivas med en parameter n. Algoritm A löser ett problem av storleken n på n sekunder medan algoritm B tar Θ(n log n) sekunder. Algoritm A är alltså snabbare än algoritm B för tillräckligt stora n. En mätning av algoritm B visar att den löser ett problem med n = 0 på sekund. Hur stort problem krävs för att algoritm A i praktiken ska vara snabbare än algoritm B? Motivera! Vad kan man dra för allmän slutsats av resultatet? Låt t A (n) och t B (n) beteckna tiden för algoritmerna A respektive B. Då gäller t A (n) = n t B (n) c n log n Eftersom t B (0) = så kan vi uppskatta konstanten c genom: c 0 log 0 = dvs (använd naturligtvis 0-logaritmen) c = /0 Vill veta när t A (n) = t B (n) dvs när är n = c n log n = 0 log n 0 = log n n = 0 0 Således: Algoritm B är snabbare än algoritm A när n > 0 0 Allmänna slutsatser: Asymptotiska resonemang (Ordo, Theta) är inte alltid så intressanta i praktiken. Vinsten med att byta ett log n mot en konstant i en algoritms komplexitet ger ofta inga praktiska tidsvinster. 8
B6. Klassen Person utgör noder i ett släktträd. Ett Person-objekt skall hålla reda på sina barn (Person-objekt) som i sin tur håller reda på sina barn etc. Metoden main och dess utskrifter visar vilka metoder klassen ska innehålla och vad de ska göra. Observera hur metoden print skriver ut noderna i preorder med en nod per rad med ett indrag proportionellt mot generationsnivå. Person-objekten ska inte hålla reda på sina föräldrar. Skriv klassen! Lotta Britta Kalle Lisa Anna Olle Pelle Lasse Eva Oskar Maja public class Person { private String name; private ArrayList<Person> children; public Person(String name) { this.name = name; this.children = new ArrayList<Person>(); public String tostring() { return name + " (" + children.size() + ")"; public void addchild(person p) { children.add(p); public void print() { print(0); public void print(int level) { for (int i=0; i<level; i++) { System.out.print(" "); System.out.println(name); for (Person p: children) { p.print(level + ); public Person getchild(int nr) { return children.get(nr); 9