LULEÅ TEKNISKA UNIVERSITET Tentamen i Program och datastrukturer Totala antalet uppgifter: 6 Lärare: Håkan Jonsson, Tomas Johansson, 491000 Resultatet anslås senast 2005-09-10 i A-huset. Tillåtna hjälpmedel: Inga. Kurskod SMD135/167 Datum 2005-08-26 Skrivtid 5 tim 1. Programming and Language Issues a) Följande klasser antas befinna sig inom varandras scope. Går de genom kompilatorn utan allvarliga felmeddelanden? Om så, vad skrivs ut då ett object av typen Pry skapas med new Pry()? Om inte, vad är fel? public class Secret{ public class Pry{ private int secret=42; public int a=2; private int reconstruct(int x){ public Pry(){ if (x==1){ Secret s=new Secret(); return secret; s.unveil(a); System.out.println(a); else{ x=x-1; unveil(x); return x; public void unveil(int c){ c = reconstruct(c); b) Förklara skillnaden mellan en variabeldeklaration som är static och en som inte är det. c) Vad skrivs ut då nedanstående C-program körs? main(){ int b[7] = {1,2,3,4,0,6,7, *p, s=0; p = &b[1]; while(*(++p)!=0) { s=s+*p; printf("*p=%d, s=%d\n",*p,s); d) Skriv en rekursiv metod public int digitsum(int d) som beräknar summan av siffrorna i ett heltal som kan antas vara positivt. Ledning: Använd operatorerna / och %, som beräknar heltalsdivision respektive ger resten vid heltalsdivision. (2p) English version a) The following classes lie within each others scope. Do they compile? If so, what is printed if an object of the type Pry is created with new Pry()? If not, what is wrong?
public class Secret{ public class Pry{ private int secret=42; public int a=2; private int reconstruct(int x){ public Pry(){ if (x==1){ Secret s=new Secret(); return secret; s.unveil(a); System.out.println(a); else{ x=x-1; unveil(x); return x; public void unveil(int c){ c = reconstruct(c); b) Explain the difference between a declaration of a variable declared static and one that is not static. c) What is printed when the C-program below is run? main(){ int b[7] = {1,2,3,4,0,6,7, *p, s=0; p = &b[1]; while(*(++p)!=0) { s=s+*p; printf("*p=%d, s=%d\n",*p,s); d) Write a function that computes the sum of all digits of a positive integer. Hint: The operators / (integer division when the operands are integers) and % (reminder at an integer division) can be used to take out the digits. (2p) 2. Theory a) Hur många beräkningssteg tar det som mest att söka i en sorterad array med n element om man använder binärsökning? b) Vad är en abstrakt datatyp? c) Vilken är den asymptotiska övre gränsen för antalet beräkningssteg som följande pseudokodsnutt som mest förbrukar uttryckt som funktion av n? for (i=1; i<n; i++) { for (j=1; j<i; j++) { Ovan står symbolen för kod som alltid utförs på O(1) (konstant) antal beräkningssteg. d) Förklara kort hur du skulle implementera en kö med hjälp av endast två stackar. (2p)
English version a) How many steps does it take, in the worst case, to search an ordered array with n elements if one employs binary search? b) What is an abstract data type (ADT)? c) What is the asymptotic upper bound on the number of steps the code snippet below takes to execute as a function of n? for (i=1; i<n; i++) { for (j=1; j<i; j++) { The symbol stands for code that takes O(1) (constant) time. d) Briefly explain how you would implement a queue using only two stacks. (2p) 3. Battery Implementera en klass Battery för att representera laddningsbara batterier. Varje nytt batteri definieras av sin maximalt möjliga laddning m (antalet Ah) och sin ändringsfaktor 0 < f < 1 (två flyttal). Till att börja med har batteriet den maximala laddningen m 0 = m. Efter laddning nummer i är den maximala laddningen istället m i = m i 1 f. Klassen ska ha följande metoder: (a) public float initialmax() som ger den initiala maximala laddningen m. (b) public float currentmax() som efter laddning i ger m i som resultat. Med andra ord: Resultatet är den laddning som batteriet kommer att få när det laddas nästa gång. (c) public float current() som ger återstående laddning l vid anropstillfället. (d) public void consume(float c) som förbrukar c enheter av den återstående laddningen l. Om c > l så ska undantaget throw new RuntimeException("Not enough capacity.") kastas. (e) public void charge() som laddar om batteriet till aktuell maximal laddning. Antag att batteriet tidigare laddats i gånger. För att räknas som en laddning krävs då att den återstående laddningen l är mindre än m i. Skulle detta inte gälla händer ingenting, dvs anropet räknas inte som en laddning och resulterar inte i några förändringar av batteriet. Annars sker en laddning vilket leder till att l = m i och att batteriets maximala laddning minskar enligt formeln i början av uppgiften. English version Implement a class Battery for representing re-chargeable batteries. Each new battery is characterized by its highest possible charge m and a factor 0 < f < 1 of change (these are two floats). The factor f determines how fast the capacity of the battery decreases when re-charged many times.
To begin with a battery has the charge m 0 = m which is also the maximun charge it can hold at that time. After having been re-charged i times the maximum charge it can hold is instead m i = m i 1 f. (Directly after re-charge number i it has the charge m i.) Add the following methods: (a) public float initialmax() that gives the maximum charge m the battery could initially hold. (b) public float currentmax() that gives the maximum charge the battery can currently hold; after having been re-charged i times this is m i. The result is, in fact, the charge the battery will get after the next re-charging. (c) public float current() that gives the current remaining charge l left in the battery. (d) public void consume(float c) that decreases the remaining charge l with c units. If c < l the exception throw new RuntimeException("Not enough capacity.") should be thrown. (e) public void charge() that re-charges the battery to the maximum charge it can currently hold. Suppose the battery has already been re-charged i times. Then, to count as a re-charge, the current charge l has to be smaller than m i. Should this not be the case, no re-charging is done and nothing happens to the battery (the call to the method is not considered a re-charging). Otherwise the battery is re-charged so that l = m i and the maximum charge the battery can hold decreases according to the formula given in the beginning of the problem. 4. A node among many Skriv en klass LinkedListNode som representerar en nod i en länkad lista. En länkad lista kan ses som ett huvud h och en svans, där huvudet är första noden i listan. Svansen är antingen tom, eller även den en länkad lista. Om svansen är en länkad lista innehåller h en referens till huvudet på svansen, som är h:s efterföljare i listan. Varje nod kan alltså ha noll eller en efterföljare, som även den är nod i en länkad lista. Konstrueraren i klassen ska ta som argument nodens efterföljare, som antingen är en referens till resten av listan eller null om noden saknar efterföljare. Skriv en metod int nrofsuccessors() som returnerar det totala antalet efterföljare en nod har. Till exempel: om noden inte har någon efterföljare ska metoden returnera 0. Om noden har en efterföljare, som i sin tur har en efterföljare, som saknar efterföljare, ska metoden returnera 2. Och så vidare. Skriv en metod boolean jumpover() som gör att noden länkar till sin efterföljares efterföljare, vilket betyder att nodens efterföljare utgår från listan. Om noden saknar två efterföljare ska metoden returnera false, annars true. English version Write the class LinkedListNode that represents a node in a linked list. A linked list can be said to consist of a head h and a tail, where the head is the first node in the list. The tail is either empty, or it is a linked list as well. If the tail is a linked list, h contains a reference to the head of the tail, which is h s successor in the list. Thus, every node can have one or zero successors, and the eventual successor is also a node in a linked list. The constructor will take the successor of the node as an argument. The argument is either a reference to the rest of the list or null if the node has no successors.
Write the method int nrofsuccessors() that returns the total number of successors that a node has. For example: if the node has no successors, the method will return 0. If the node has a successor, that in turn has another successor which lacks successor, the method will return 2. And so on. Write the method boolean jumpover() that makes the node link to its successors successor, which means that the node s successor is removed from the list. If the node lacks successors the method should return false, otherwise true. 5. Sea cruise Figure 1: UML diagram for Problem 5. Implementera de klasser och gränssnitt för båtar som beskrivs i UML-diagrammet (figur 1) enligt följande: (a) Den abstrakta klassen Boat: En konstruerare som tar båtens deplacement och byggår som argument (i form av heltalsvariabler). Om byggåret är äldre än 1980 ska texten "This boat is really old!" skrivas ut. En abstrakt metod cost() som returnerar båtens kostnad. (b) Underklassen MotorBoat: cost() returnerar båtens kostnad enligt formeln 20 000*deplacement, eftersom större båtar ju alltid är bättre. MotorBoat ska även implementera gränssnittet MotorVehicle, som innehåller följande abstrakta metoder: fillup(int litres), som fyller på litres liter bensin. spend(int litres), som spenderar litres liter bensin. Om bensinen tar slut ska undantaget EmptyException kastas tillsammans med ett lämpligt felmeddelande. Ett fordon kan förstås aldrig ha en negativ mängd bensin. Du får anta att klassen EmptyException redan är skriven. Ett motorfordon innehåller ingen bensin när det skapas. (c) Underklassen SmallMotorBoat: Ska fungera som MotorBoat, med den enda skillnaden att man aldrig kan skapa en SmallMotorBoat med ett deplacement större än 100. English version Implement the classes and interfaces for boats described in the UML diagram (Fig. 1) according to the following: (a) The abstract class Boat:
A constructor that thakes the deplacement and construction year of the boat as arguments (in the form of integers). If the construction year is older than 1980, the text "This boat is really old!" be written. An abstract method cost() that returns the cost of the boat. (b) The subclass MotorBoat: cost() should return the cost of the boat according to the formula 20 000*deplacement, since bigger boats obviously are better than smaller ones. MotorBoat should also implement the interface MotorVehicle, that contains the following abstract methods: fillup(int litres), That fills the tank with the amount litres. spend(int litres), that spends the amount litres. If the boat runs out of gas the exception EmptyException should be thrown with an appropriate error message. A vehicle can never have a negative amount of gas. You may assume that the class EmptyException is already written. A motor vehicle does not contain any gas when it is created. (c) The subclass SmallMotorBoat: Will work just as MotorBoat, the only difference is that you can never create a SmallMotorBoat with a deplacement bigger than 100. 6. Parsing trees Implementera en pareser som känner igen binära träd beskrivna av följande BNFgrammatik: T ::= "Node" T <const> T "Leaf" Använd lexikalanalysatorn ( tokenizern ) som finns bifogad och se till att din lösning passar in i den kod som finns i slutet av denna uppgift. <const> står för en godtycklig numerisk konstant medan "Node" and "Leaf" är ord som måste överenstämma exakt. English version Implement a parser that recognizes descriptions of binary trees according to the following BNF-grammar: T ::= "Node" T <const> T "Leaf" Use the tokenizer appended to the exam and make your solution fit into the context given below. <const> stands for a[ny] numerical constant while "Node" and "Leaf" are words that must match exactly. public abstract class BinTree { private static Tokenizer token; public static boolean accept(reader input) { boolean ok; token = new NaturalTokenizer(input); token.get(); ok = bt(); return ok;
private static boolean bt() { // You should implement this method.
Tokenizer.java import java.io.*; public class Tokenizer { private StreamTokenizer tokens; protected int type; public static final int ID = 1, NUMBER = 2, RIGHTPAREN = 3, LEFTPAREN = 4, ENDOFFILE = 5, PLUS = 6, MINUS = 7, ASTR = 8, SLASH = 9, LEFTBRACE = 10, RIGHTBRACE = 11, COMMA = 12, ILLEGAL = -1; public Tokenizer(Reader input) { tokens = new StreamTokenizer(input); tokens.ordinarychar( - ); tokens.ordinarychar( / ); public void get() { try {type = tokens.nexttoken(); catch (IOException e) {throw new RuntimeException(e.toString()); public int kind() { switch (type) { case StreamTokenizer.TT_WORD : return ID; case StreamTokenizer.TT_NUMBER : return NUMBER; case StreamTokenizer.TT_EOF : return ENDOFFILE; default : switch ((char) type ) { case ( : return LEFTPAREN; case ) : return RIGHTPAREN; case + : return PLUS; case - : return MINUS; case * : return ASTR; case / : return SLASH; case { : return LEFTBRACE; case : return RIGHTBRACE; case, : return COMMA; default: return ILLEGAL; public String ident() {return tokens.sval; public double numbervalue() {return (double) tokens.nval; public String tostring() {return tokens.tostring();
Errata SMD135/167, 2005-08-26 Problem 5: In case an attempt is made to create a SmallMotorBoat with a deplacement larger than 100 it is not defined what should happen. A correct solution should check the deplacement and take at least some action if it is larger than 100. Problem 6: NaturalTokenizer in the given code (the class BinTree) should be Tokenizer.
Solutions SMD135/167, 2005-08-26 1 a) Yes, they compile and the output will be 2, the initial value of a. A primitive variable used as an arguments in a call to a method does not change as a result of the call. b) The keyword static makes the variable common to all objects. Assignments made by one object is noticable to all other objects. (Non-static variables come in one unique and independent copy per instantiated object.) c) The output will be 0 7 d) The operation d % 10 picks out the last digit of d. public int digitsum(int d) { if (d < 10) { return d; else { return (d % 10) + digitsum(d / 10); 2 a) O(log n) steps. b) An abstract data type (ADT) is a well-defined set of values, operations on the values, and axioms describing the semantics of the operations. c) O(n 2 ) steps. d) The queue has the operations top, empty, enqueue, and dequeue and an answer has to make clear how the two stacks are used to support these operations. Let S in and S out denote the two stacks, which are initially empty. 3 Solution 4 Solution 5 Solution 6 Solution enqueue The element inserted is pushed on top of S in (hence its name). empty Is true if both stacks are empty, and otherwise false. top If the queue is empty, an error condition has occured. If not, the element on top of S out is returned. If S out is empty, a swap operation is performed where the elements of S in are popped and pushed onto S out one at a time and in order. The top of S out is then returned. Notice that only a copy is retured; the element is not dequeued. dequeue If the queue is empty, an error condition has occured. If not, the top of S out is popped. Should S out be empty the same swap as that mentioned above is first performed. All operations of this particular implementation takes O(1) time to perform in an amortized sense.