1(4) Institutionen för datavetenskap LUNDS TEKNISKA HÖGSKOLA Tentamen, EDAA30 Programmering i Java – fortsättningskurs 2015–01–12, 14.00–19.00 Anvisningar: Denna tentamen består av 5 uppgifter. Preliminärt ger uppgifterna 1. 2. 3. 4. 5. 1+1+1+1+1=5p 2+1+1=4p 10 p 6p 2 + 1 + 5 + 3 = 11 p - Tillåtna hjälpmedel: Java snabbreferens och Utdrag ur JCF ämnat för EDAA30 ht 14. Bifogat tentamen finns kompletterande dokumentation för gränssnitten Comparable, Iterable och Map. - För full poäng ska dina lösningar inte vara onödigt ineffektiva. - När rättningen är klar meddelas detta på kursens hemsida (cs.lth.se/edaa30). Lösningsförslag kommer att finnas tillgängliga efter skrivtidens slut. 1. Ange om följande påståenden är sanna eller falska. För poäng ska dina svar motiveras ordentligt. a) Betrakta följande algoritm: long sum = 0; for (int i = 0; i <= n; i++) { int prod = 1; for (int j = 0; j < i; j++) { prod = prod * x; } sum = sum + prod; } Antag att det då n = 100000 tar ca 3 sekunder att exekvera algoritmen på en viss dator. Att exekvera algoritmen för n = 500000 på samma dator tar då ca 15 sekunder. b) Man kan effektivt implementera en stack med hjälp av en vektor. c) Binärsökning passar bra vid sökning efter ett godtyckligt element i en sorterad enkellänkad lista. d) Det passar bra att använda en heap för att implementera den abstrakta datatypen mängd (eng. set). e) En heap (minheap) implementeras ofta med en vektor. Elementen i denna vektor är alltid sorterade i stigande ordning. 2. a) Både mergesort och quicksort är rekursiva algoritmer av typen ”söndra och härska”. Beskriv kort, men begripligt, i ord och i pseudokod i grova drag hur mergesort fungerar. Parametrar, basfall och rekursiva anrop (uppdelningen i enklare delproblem) ska framgå i pseudokoden. Gå för övrigt inte in på några detaljer. b) Quicksort påminner en hel del om mergesort, men uppdelning i enklare delproblem skiljer sig åt. På vilket sätt? 2(4) c) Nämn någon fördel Mergesort har jämfört med Quicksort. Nämn också någon fördel Quicksort har jämfört med Mergesort. 3. Antag att vi i någon tillämpning behöver lagra en mängd med heltal. I den aktuella tillämpningen är det vanligt att flera tal i följd lagras; till exempel kan en mängd innehålla talen 1–4, 18–23, 25, 28–57. Detta ska man utnyttja för att spara minnesutrymme. Interfacet IntSet beskriver en sådan mängd: public interface IntSet { /** Tar reda på om nbr finns i mängden. */ boolean contains(int nbr); /** Sätter in nbr i mängden om det inte redan finns. Returnerar true om nbr kan sättas in i mängden, i annat fall false. */ boolean insert(int nbr); /* Tar reda på antal tal i mängden. */ public int size() } Din uppgift är att skriva en klass som implementerar interfacet IntSet enligt följande anvisningar: • Mängden ska implementeras med en enkellänkad lista. Elementen i listan ska vara objekt av en statiskt nästlad klass Node: private static class Node { private int min; private int max; private Node next; private Node(int min, int max) { this.min = min; this.max = max; next = null; } } Attributen min anger det minsta talet i en följd och max anger det största talet. • Elementen ska vara sorterade i växande ordning. • Först i listan ska det finnas ett element där min = max = Integer.MIN_VALUE, sist i listan ett element med min = max = Integer.MAX_VALUE. Dessa element underlättar eftersom varje element i listan kommer att ha en föregångare och en efterföljare. Listan i ett tomt IntSet innehåller båda dessa element. • Exempel: MIN_VALUE MIN_VALUE 1 4 18 23 25 25 28 57 MAX_VALUE MAX_VALUE null Figur 1: Lista som beskriver mängden 1–4, 18–23, 25, 28–57 • Flera fall kan inträffa när man lägger in ett tal i mängden. Exemplen från listan i figur 1: – insert(20): talet finns redan, ingenting ska inträffa. – insert(24): två element slås ihop till ett. – insert(17): min-värdet i ett existerande element ska ändras. – insert(5): max-värdet i ett existerande element ska ändras. – insert(10): ett nytt element ska läggas in på rätt plats i listan. 3(4) 4. Ett binärt aritmetiskt uttryck kan representeras av ett strikt binärt träd. Se fig. 2. + * 7 - 5 9 2 Figur 2: Binärt träd som representerar ett aritmetiskt uttryck. Följande klasser representerar ett binärt uttrycksträd. För enkelhets skull har vi i den här uppgiften låtit både operatorer och heltal representeras av en teckensträng. public class ExprTree { private ExprNode root; // refererar till roten i trädet // konstruktor och övriga metoder som inte är intressanta i den här uppgiften /** Returnerar värdet av det aritmetiska uttryck som trädet representerar. Om trädet är tomt ska 0 returneras. */ public int value() { ... } private static class ExprNode { private String element; // nodens innehåll private ExprNode left; // refererar till vänster barn private ExprNode right; // refererar till höger barn private ExprNode(String element) { this.element = element; left = null; right = null; } } } Implementera metoden value med rekursiv teknik. Ledning och anvisningar: • Gå igenom trädet i postorder vid beräkningen av värdet. Exempel: Det aritmetiska uttrycket i figur 2 har värdet 42. • Det är tillåtet (och lämpligt) att lägga till en privat metod antingen i klassen ExprTree eller i klassen ExprNode. • För omvandling från typen String till int kan man använda följande metod i klassen String: static int parseInt(String s) 5. a) Man kan använda en öppen hashtabell (eng. separate chaining) för att implementera den abstrakta datatypen lexikon (eng. Map). Förklara vad som menas med en öppen hashtabell och vad som skiljer den från en sluten hashtabell. b) Föreslå någon annan datastruktur som kan användas för att på ett effektivt sätt implementera den abstrakta datatypen lexikon (eng. Map). Motivera ditt val. c) Ett lexikon (eng. Map) kan användas för att lösa följande problem: 4(4) En chokladask innehåller olika slags praliner. Tillverkaren funderar på att byta ut några av pralinerna mot nya och har därför genomfört en omröstning för att avgöra hur populära de olika pralinerna är. Rösterna är strängar och finns i en lista av typen List<String>: [Trillingnöt, Trillingnöt, Gräddnougat, Romrussin, ..., Körsbär i likör, Gräddnougat] Det resultat som ska presenteras är antalet röster de olika pralinerna fått. [Trillingnöt 28, Romrussin 2, Gräddnougat 31, ..., Körsbär i likör 5] Här är programkod som löser problemet: List<String> list = new ArrayList<String>(); // Här är programrader för att läsa in och lagra de olika rösterna (pralinnamn) i list Map<String, Integer> resultMap = VoteCounter.result(list); System.out.println(resultMap); Skriv färdigt klassen VoteCounter med den statiska metoden result som kan användas i problemet ovan. Metoden result ska vara generisk så att den ska kunna användas för att räkna annat än strängar. public class VoteCounter { /** Räknar antal olika slags element i listan votes och returnerar resultatet i en map med par av element och dess antal förekomster. */ public static <E> Map<E, Integer> result(List<E> votes) { ... } } d) I deluppgift c anropas metoden result med en lista av typen List<String> som argument. Antag att list istället har typen List<Praline> och innehåller Praline-objekt istället för strängar. Klassen Praline set ur så här: public class Praline { private String name; private int manCost; // tillverkningskostnad (öre) public Praline(String name, int manCost) { this.name = name; this.manCost = manCost; } public String getName() { return name; } public int getManCost() { return manCost; } public String toString() { return name; } } Utskriften blir då inte längre den förväntade. Förklara varför. Gör det tillägg som krävs för att anropet av result med en lista av typen List<Praline> som argument ska fungera.