Data- och Programstrukturer Provmoment: Ladokkod: Tentamen ges för: Omtentamen NDP011 Systemarkitektprogrammet 7,5 högskolepoäng Namn: (Ifylles av student) Personnummer: (Ifylles av student) Tentamensdatum: 2013-11-14 Tid: 09.00 13.00 Hjälpmedel: Inga hjälpmedel Totalt antal poäng på tentamen: För att få respektive betyg krävs: G >= 22 VG >= 34 45 poäng Allmänna anvisningar: Numrera sidorna samt börja varje ny uppgift på nytt blad. Skriv personnummer på försättsblad och alla inlämnade sidor. Var tydliga, kortfattade och konkreta i era svar. Skriv tydligt (oläsligt = fel)! På programmeringsuppgifterna är principen viktigare än syntaxen. Poängavdrag kan ges för onödigt komplicerade eller ostrukturerade lösningar. Kommandon från bibliotek (även Roberts) får normalt användas fritt. Om du är osäker skriv ner vad kommandot förväntas göra. Inkluderingar behöver aldrig anges om det inte framgår av frågan. Rättningstiden är som längst tre veckor Viktigt! Glöm inte att skriva namn på alla blad du lämnar in. Lycka till! Ansvarig lärare: Anders Gidenstam Karl Jansson Telefonnummer: 033 435 4214 033 435 4399
1. Definiera följande begrepp och förklara kontexten de används inom: a. Lastfaktor (eng. load factor) b. Tidskomplexitet (eng. Computational complexity) c. Trädbalansering (eng. Tree-balancing) d. Hash funktion (eng. Hash funktion). (4p) 2. Betrakta följande rekursiva funktion: void RecursiveSubset(string str, string res) { if (!StringLength(str)) { printf("{%s}\n", res); } else { RecursiveSubset(str+1, Concat(res, CharToString(str[0]))); RecursiveSubset(str+1, res); } } void ListSubsets(string str) { int i = 0; RecursiveSubset(str, ""); } Visa i ord och bild hur resultatet av följande anrop till ListSubsets() beräknas. ListSubset("ABC"); En lämplig nivå är att visa vilka funktionsanrop som sker, deras argument och eventuella utskrifter. (2p) Beskriv i (c en mening den rekursiva idén bakom algoritmen ovan. (1p) c) Hur många anrop till RecursiveSubset()genereras av anropet ListSubset(str) för en sträng str av längd N. Du behöver inte nödvändigtvis finna ett slutet uttryck i strängens längd N, att generalisera fram ett mönster från små instanser är också ok. (2p) 2
3. En lövnod i ett binärt träd är en nod helt utan barn (dvs. med två tomma delträd). Antag att vi har en enkel implementation där ett träd är deklarerat enligt följande: typedef struct treet { int info; struct treet *left, *right; } *treet; Skriv en funktion med prototypen int countleaves(treet tree); som beräknar antalet lövnoder i trädet. 4. En prioritetskö (eng. priority queue) är en abstrakt samlingsdatatyp (ADT) med operationerna void Enqueue(pqueueADT pqueue, int newvalue) Sätter in angivet värde (newvalue) i prioritetskön. int DequeueMax(pqueueADT pqueue) Tar bort och returnerar elementet med högst prioritet i prioritetskön. Förklara i ord och bild hur man kan implementera en prioritetskö med Ett osorterad länkad lista (eng. unsorted linked list). Var ett nytt element läggs till är valfritt. En hög (eng. heap) (implementerad i en array som i laboration 2). Ange och förklara även respektive operations tidskomplexitet (worst-case) för båda implementeringarna uttryckt i antalet element i symboltabellen, N. Tips: En före och efter bild av hur datastrukturen ser ut i minnet med förklaring av ej uppenbara förändringar är en lagom nivå för att visa hur en operation fungerar, kod behövs inte. (8p) 3
5. En symboltabell (eng. dictionary) är en abstrakt samlingsdatatyp (ADT) med (bland annat) operationerna void Insert(symtabADT table, string key, void *value) Sätter in angivet nyckel, värde par (key, value) i symboltabellen. Om nyckeln redan finns så uppdateras det associerade värdet till value. bool Lookup(symtabADT table, string key, void **value) Slår upp nyckeln (key) i symboltabellen. Om nyckeln finns i symboltabellen placeras värdet (av typ void*) som hör ihop med den i minnesutrymmet som argumentet value pekar på och TRUE returneras. Om nyckeln inte finns i symboltabellen returneras FALSE. En möjlig implementation av en symboltabell är en sorterad enkellänkad lista. Nedan följer typdeklarationer för en sådan: typedef struct symtabcdt *symtabadt; typedef struct _cellt { string key; void *value; struct _cellt *next; } cellt; struct symtabcdt { cellt *first; }; void Insert(symtabADT table, string key, void *value) för en symboltabell implementerad som en sorterad enkellänkad lista i C. bool Lookup(symtabADT table, string key, void **value) för en symboltabell implementerad som en sorterad enkellänkad lista i C. Var noggrann med avseende på specialfall och minneshantering. Rita gärna strukturen på ett kladdpapper innan du skriver koden. 4
6. En dubbeländad kö (eng. deque) är en ADT som kan bete sig som en kö åt båda hållen, eller, mer lätt begripligt, båda ändarna beter sig som stack:ar, dvs. man kan lägga till och ta bort element i båda ändarna. Gränssnittet till en dequeadt är givet nedan. /* File: deque.h This interface defines a double-ended queue. /* Type: dequeelementt typedef double dequeelementt; /* Type: dequeadt typedef struct dequecdt *dequeadt; /* Function: NewDeque * Usage: deque = NewDeque(); dequeadt NewDeque(void); /* Function: FreeDeque * Usage: FreeDeque(deque); void FreeDeque(dequeADT deque); /* Function: PushLeft * Usage: PushLeft(deque, element); * This function pushes the specified element onto the left end of the deque. void PushLeft(dequeADT deque, dequeelementt element); /* Function: PushRight * Usage: PushRight(deque, element); * This function pushes the specified element onto the right end of the deque. void PushRight(dequeADT deque, dequeelementt element); /* Function: PopLeft * Usage: element = PopLeft(deque); * This function pops the leftmost element from the deque and returns * that value. If the deque is empty when PopLeft is called, * the function calls Error with an appropriate message. dequeelementt PopLeft(dequeADT deque); /* Function: PopRight * Usage: element = PopRight(deque); * This function pops the rightmost element from the deque and returns * that value. If the deque is empty when PopRight is called, * the function calls Error with an appropriate message. dequeelementt PopRight(dequeADT deque); /* Functions: DequeIsEmpty, DequeIsFull * Usage: if (DequeIsEmpty(deque))... * if (DequeIsFull(deque))... * This functions test whether the deque is empty or full. * A dynamic deque will never become full (since we assume the system memory * will never be exhausted). bool DequeIsEmpty(dequeADT deque); bool DequeIsFull(dequeADT deque); 5
Ett sätt att implementera en dubbeländad kö är med en dubbellänkad lista. Definitionen av dequecdt och de interna noderna av typen nodet blir då typedef struct _nodet { dequeelementt elem; struct _nodet *next; struct _nodet *prev; } nodet; struct dequecdt { nodet *leftend; nodet *rightend; }; Den dubbellänkade listan skall inte vara cirkulär utan kedjan av noder slutar vid första och sista noden. Således skall en tom dubbeländad kö representeras av frånvaron av noder. bool DequeIsEmpty(dequeADT deque); i C för dequecdt och nodet definitionerna ovan. (3p) bool PushLeft(dequeADT deque, dequeelementt element); i C för dequecdt och nodet definitionerna ovan. c) dequeelementt PopRight(dequeADT deque); i C för dequecdt och nodet definitionerna ovan. Var noggrann med avseende på specialfall och minneshantering. Rita gärna strukturen på ett kladdpapper innan du skriver koden. 6