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.