Tentamen Programmeringsteknik II och NV2 (alla varianter) 2008-12-10 Skrivtid: 0800-1300 Inga hjälpmedel. Tänk på följande Maximal poäng är 40. För betygen 3 krävs 18 poäng. För betygen 4, 5 kommer något högre gränser än normalt användas. Skriv läsligt! Använd inte rödpenna! Skriv bara på framsidan av varje papper. Börja alltid ny uppgift på nytt papper. Lägg uppgifterna i ordning. Skriv uppgiftsnummer och namn på alla papper. Skriv inte längst upp i vänstra hörnet - det går inte att läsa där efter sammanhäftning. Fyll i försättssidan fullständigt. 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. Programkod skall vara läslig dvs den skall vara vettigt strukturerad och indenterad. Namn på variabler, metoder, funktioner, klasser etc skall vara beskrivande men kan ändå hållas ganska korta. Såvida inget annat anges så får man bygga på lösningar till föregående uppgifter även om dessa inte har lösts. Det är tillåtet att införa hjälpmetoder och hjälpklasser. Uttrycket skriv en metod som skall alltså inte tolkas så att lösningen inte får struktureras med hjälp av flera metoder. Lycka till! 1
Uppgifter 1. Följande nycklar är givna: 12, 8, 5, 7, 17, 31, 14, 9, 13, 21 (a) Använd nycklarna (i given ordning) för att bygga upp ett binärt sökträd. Hur många jämförelser krävs i genomsnitt för att återfinna en lagrad nyckel i just detta träd? (2p) (b) Använd nycklarna (i given ordning) för att konstruera en hashtabell med länkad kollisionshantering. Tabellstorleken skall vara 7. Hur många jämförelser krävs krävs i genomsnitt för att upptäcka att en given nyckel inte finns lagrad i just denna tabell? (Du får göra rimliga antaganden om hashfunktionen) (2p) (c) Hashtabeller och binära sökträd är två alternativa implementationer av avbildningar. Vad är fördelarna med hashtabeller och vad är fördelarna med binära sökträd? (1p) 2. (a) Vad menas med en abstrakt metod i Java? Vad innebär det för klassen som har metoden som abstrakt? (1p) (b) Vad menas med interface i Java? Ge exempel på användning av interface. (1p) 3. Vad skrivs ut av följande kod? Svaret skall motiveras! class Bas { int x = 1; void foo(int y) { System.out.println("Bas-foo : " + y); class Sub extends Bas { int x=2; void foo(int y) { System.out.println("Sub-foo: " + y); public class VadSkrivs { public static void main(string [] args) { Bas b = new Sub(); Sub s = (Sub) b; 2
b.foo(b.x); b.foo(s.x); s.foo(b.x); s.foo(s.x); (2p) 4. Nedanstående klass används för att implementera en lista där varje element innehåller ett värde (int). public class List { protected static class ListNode { int value; ListNode next; ListNode(int v, ListNode n) { value = v; next = n; public String tostring() { return "" + value; protected ListNode head; protected static class NoSuchKeyException extends RuntimeException { public NoSuchKeyException(String id) { super(id); public List() { head = null; public List(TreeSet t) { // En konstruktor som skapar en lista utifrån ett träd av // typen TreeSet som är deklarerat i nästa uppgift. Alla // nycklar som finns i trädet ska läggas in listan i // numeriskt stigande ordning. Trädet är ett binärt // sökträd. Du får använda allt som finns deklarerat i // trädklassen även om du inte implementerar det. public int size() { 3
return size(head); public int size(listnode l) { if (l == null) return 0; else return 1 + size(l.next); public int frequency(int v) { // räknar hur många gånger v finns i listan public String tostring() { return "[" + tostring(head) + "]"; protected String tostring(listnode l) { if (l==null) return ""; else if (l.next == null) return "" + l.value; else return l.value + ", " + tostring(l.next); public boolean in(int k) { // undersök om given nyckel finns i listan public void addfirst(int v) { // lägger in först i listan head = new ListNode(v, head); public void addinorder(int v) { head = addinorder(head, v); public ListNode addinorder(listnode l, int v) { if(l == null l.value > v) return new ListNode(v, l); // först else { l.next = addinorder(l.next,v); return l; public void remove(int v) { // tar bort första förekomsten av v i listan. 4
// Om v inte finns i listan ska ett undantag // av typ NoSuchKeyException kastas public void reverse() { // vänd på listan så att noderna kommer i // motsatt ordning, dvs den sista först // och den första sist osv. public List intersect(list l) { // skapar en ny lista som innehållar endast de // nycklar som finns i BÅDA listorna, dvs snittet av // mängdernas innehåll. Det ska inte finnas dubbletter // av nycklarna i den nya listan. De ingående listorna // påverkas inte av operationen. public static void main(string[] args) { List l = new List(); l.addfirst(1); l.addfirst(2); l.addfirst(3); l.addfirst(4); List k = new List(); k.addfirst(4); k.addfirst(2); k.addfirst(7); k.addfirst(5); System.out.println(l); System.out.println(l.size()); System.out.println( "4: " + l.in(4)); System.out.println( "6: " + l.in(6)); l.reverse(); System.out.println(l); System.out.println(k); List m = l.intersect(k); System.out.println(m); /* Output: [4, 3, 2, 1] 4 4: true 6: false [1, 2, 3, 4] [4, 2, 7, 5] [2, 4] */ I samtliga deluppgifter får du använda allt som finns implementerat i klassen. Om det inte är implementerat måste du implementera det själv. 5
(a) Implementera remove-metoden som skall ta bort angiven nyckel från listan. Se deklaration av metoden ovan för detaljerad specifikation. (3p) (b) Implementera metoden intersect enligt kommentarerna i koden ovan. (3p) (c) Implementera konstruktorn List(TreeSet t) enligt kommentarerna i koden ovan. (4p) 5. Följande klass används för att representera en mängd med heltal. Mängden är organiserad som ett binärt sökträd. public class TreeSet { private Node root = null; public static class Node { int data; Node left, right; public Node(int d) { data = d; left = null; right = null; // en inorder iterator private class TreeIterator implements Iterator<Integer> { private Node current = null; // konstruktor, sätt till första noden. public TreeIterator() { // kolla om det finns fler noder public boolean hasnext() { // hämta data från nästa nod, uppdatera current 6
public Integer next() { ; public void remove() { throw new UnsupportedOperationException ("Remove not implemented"); // ny iterator public Iterator<Integer> iterator() { return new TreeIterator(); public TreeSet copy() { // kopiera trädet TreeSet t = new TreeSet(); t.root = copy(root); return t; private Node copy(node t) { if (t == null) return null; else { Node ny = new Node(t.data); ny.left = copy(t.left); ny.right = copy(t.right); return ny; public void add(int k) { root = add(k, root); private Node add(int k, Node r) { // lägg in värdet k i trädet som pekas ut av r public String tostring() { return tostring(root); private String tostring(node r) { String res = ""; if ( r!=null ) { res = tostring(r.left) + " " + r.data + " " + tostring(r.right); 7
return res; public boolean isbalanced() { // undersöker om trädet är balanserat public boolean equals(treeset t) { // undersöker om två träd är lika, // dvs ser lika ut och med // samma innehåll i motsvarande noder. public void levelordertraverse() { // gör en nivåordningstraversering av trädet // skriv ut värdet i noderna som du passerar. // se utskrift i exemplet nedan public int externalpathlength(){ // beräknar och returnerar den externa // väglängden i trädet. public TreeSet union(treeset t) { // skapar ett nytt träd som innehåller alla nycklar // som finns antingen i detta träd eller i trädet t. // naturligtvis inga dubbletter. De båda ingående // träden påverkas inte på // något sätt av operationen. public static void main(string[] args) { TreeSet t = new TreeSet(); t.add(7); t.add(1); t.add(4); t.add(12); t.add(9); t.add(15); System.out.println("t : " + t); System.out.println("External pathlength: " + t.externalpathlength(); System.out.println("t.equals(t)) : " + t.equals(t)); System.out.print("Nivåordning: "); t.levelordertraverse(); System.out.println("t.isBalanced() : " + t.isbalanced()); TreeSet u = new TreeSet(); u.add(12); u.add(7); u.add(1); u.add(4); u.add(9); u.add(15); 8
System.out.println("u : " + u); System.out.println("u.equals(t)) : " + u.equals(t)); System.out.print("Nivåordning: "); u.levelordertraverse(); System.out.println("u.isBalanced() : " + u.isbalanced()); Iterator<Integer> i = u.iterator(); System.out.print("Iterator: "); while (i.hasnext()) { int tal = i.next(); System.out.print(tal + " "); System.out.println(); TreeSet v = u.union(t); // unionen av träden, // innehåller 1,4,7,9.12 // och 15 eftersom // dessa finns i någon // av u och t /* Output: kursa$ java TreeSet t : 1 4 7 9 12 15 External pathlength: 27 t.equals(t)) : true Nivåordning: 7 1 12 4 9 15 t.isbalanced() : true u : 1 4 7 9 12 15 u.equals(t) : false Nivåordning: 12 7 15 1 9 4 u.isbalanced() : false Iterator: 1 4 7 9 12 15 kursa$ */ I samtliga deluppgifter får du använda allt som finns implementerat i klassen. Om det inte är implementerat måste du implementera det själv. (a) Implementera add-metoden som lägger in en nyckel i trädet Om den redan finns kastas ett undantag. (3p) (b) Implementera metoden union som skall returnera ett nytt träd som innehåller alla nycklar som finns i något av de båda träden 9
som man tar unionen av. Det får naturligtvis inte bli dubbletter i resultatträdet. Resultatträdet skall vara ett binärt sökträd. (3p) (c) Implementera den iterator som är deklarerad i trädklassen. Du skall alltså implementera konstruktorn, metoderna hasnext och next på det sätt som en iterator förväntas fungera. Metoden next ska ge nycklarna i inorder-ordning. (4p) 6. I programbilagan Parser.java finns ett program som läser aritmetiska uttryck, representerar dem internt i ett träd, evaluerar trädet och skriver värdet. Uttrycken är begränsade till att bestå av konstanter, variabler, additions- och multiplikationsoperatorer, monärt minus samt parenteser och tilldelningar. Vissa delar av koden är utelämnade. (Observera att metoden som hette prim i inlämningsuppgiften heter factor här.) (a) I programbilagans factor-metod hanteras unärt minus på så sätt att man rekursivt anropar factor för att beräkna det som ska negeras. Vad händer om man byter ut if ( st.ttype == - ) { result = new Uminus(factor()); mot if ( st.ttype == - ) { result = new Uminus(assignment()); (b) Om jag skriver 2+4%5 (1p) respektive 2**5 så är ju båda uttrycken syntaxtiskt felaktiga. Förklara i båda fallen vilken metod i programbilagans parser som kommer att kasta ett undantag. (1p) 10
(c) Implementera operatorn absolutbelopp som ska ta absolutbeloppet av ett värde. Inför alla klasser och metoder som behövs. Operatorn ska fungera som förväntat och ge felutskrifter vid felaktiga parametrar. Exempel: :>-3 = a -3 :>4 + 3 * sin( a +2) - 1 9.876772823989416 Du skall således skriva en klass Abs som subklass till lämplig bas med de metoder som behövs för evaluering, konvertering till sträng etc. Du skall också modifiera Parser-klassen så att operatorn känns igen och så att uttrycksträdet kan byggas med Absobjekt. (4p) 7. Denna uppgift skall inte göras av dem som går Kandidatprogrammet i ma/fy/da, kurskod 1TD722, inte heller av dem som går STSprogrammet kurskoder 1TD772 eller 1TS080 Antag att du har följande deklarationer i C /* skapa en struct som beskriver en person i en listnod */ struct node { char *namn; char *adress; int medlemsnummer; struct node *next; typedef struct node node; /* några listfunktioner */ void print(node *l); /* lägg in i listan, parametrarna är bl. a. namn, adress, medlemsnummer */ node *insertfirst(node *l, char *nm, 11
char *adr, int nr); node *insertinorder(node *l, char *nm, char *adr, int nr); int isempty(node *l); node* clear(node *l); node* removefirst(node *l); node* remove(node *l, char *namn); (a) Implementera funktionen isempty som testar om en lista är tom eller inte. (1p) (b) Implementera funktionen insertfirst som lägger in en nod först i listan. Noden ska innehålla angiven nyckel (personens namn). Du ska säkerställa nodens integritet, dvs se till att ingen annan har någon pekare till det namn som lagras i noden. (2p) (c) Implementera funktionen removefirst som tar bort första noden i listan. Allt använt minne skall frigöras. (2p) 12