QUESTION 1 (25 marks) Short answer questions you do not need to provide any justifications for your answers. Just fill in your answer in the space provided. For each correct answer, 1 mark will be awarded. (1.1) True or false: Testing can never be used to prove that a program is correct. (1.2) Which of the following data structures is more appropriate as an implementation of the queue ADT: singly linked list or doubly linked list? Answer: singly linked list (1.3) True or false: Prim s algorithm is asymptotically faster than Dijkstra s algorithm when running on connected graphs. (1.4) True or false: The worst-case running time of selection sort, when applied to an array that is already sorted, is in Θ(n). (1.5) True or false: In divide-and-conquer algorithms, a global optimization problem is solved by making a sequence of local greedy choices. (1.6) True or false: Breadth-first search algorithm works on both cyclic and acyclic graphs. Answer: true 3 of 15
(1.7) True or false: Dijkstra s algorithm discussed in class does not work correctly if some edge weights are negative. Answer: true (1.8) Using the most accurate asymptotic notation, give the worst-case running time of randomized partitioning (in quick sort) on an input array of length n. Answer: Θ(n) (1.9) Using asymptotic notation, give the worst-case running time of testing whether two vertices are neighbors in a graph G = (V, E) if the graph is implemented with adjacency lists. Answer: Θ( V ) (1.10) True or false: Compared to worst-case analysis, amortized analysis provides a more accurate upper bound on the performance of an algorithm. (1.11) Is black box testing a type of dynamic or static testing? Answer: dynamic testing (1.12) True or false: In some exceptional cases a loop invariant for a while loop may not be true before the first execution of the loop. (1.13) Using asymptotic notation, what is the memory space required to represent a graph G = (V, E) using an adjacency-list? Answer: Θ( V + E ) 4 of 15
(1.14) Consider an undirected connected acyclic graph G that has n vertices. How many spanning trees can be identified in G? Answer: one (1.15) Consider a graph G = (V, E). Let deg(v) denote the degree of vertex v V. What is the result of expression v V deg(v) in terms of E and V? Answer: 2 E (1.16) True or false: Every connected graph has a unique predecessor subgraph. (1.17) A telephone company plans to connect its switching centers together using fiber optics. When connecting switching centers, it is required to have a path between every pair of them. The telephone company is looking for an interconnection topology that minimizes the amount of fiber required to connect its switching centers. Which algorithm discussed in class can be used to help the telephone company find the optimal topology? Answer: Prim s Algorithm Minimum Spanning Tree (1.18) An array A is used to implement a binary heap of size n. Give the left child of the heap element stored in location i of the array. Answer: 2i + 1 null if 2i + 1 n 1 (1.19) Which type of hash table is susceptible to primary clustering: hash table with open addressing or hash table with chaining? Answer: hash table with open addressing 5 of 15
(1.20) Which of the following data structures is more appropriate as an implementation of the priority queue ADT: linked list, stack or heap? Answer: heap (1.21) (5 marks) Consider functions f (n) and g(n) as given below. Use the most precise asymptotic notation to show how function f is related to function g in each case (i.e., f?(g)). For example, if you were given the pair of functions f (n) = n and g(n) = n 2 then the correct answer would be: f o(g). To avoid any ambiguity between O(g) and o(g) notations due to writing, use Big-O(g) instead of O(g). f (n) g(n) Relation n 3 + 2n + 1 1 100 n3 + n log n f Θ(g) 2 n n 1000 f ω(g) log 2 n log 3 n f Θ(g) 2 n 3 n f o(g) 0.5 n 1 f o(g) 6 of 15
QUESTION 2 (7 marks) The following questions deal with binary search trees. (2.1) (4 marks) Give pseudocode for a recursive algorithm that searches for a given key in a binary search tree. If the key is found in the tree, your algorithm should return the value associated with that key; otherwise it should throw a NotFoundException exception. The following definitions are assumed: public class BST<E,V> { protected bstnode<e,v> root; protected class bstnode<e,v> { E key; V value; bstnode<e,v> left; bstnode<e,v> right; } } public V search(e key) throws NotFoundException {return search(root, E);} private V search(bstnode<e,v> T, E key) throws NotFoundException; where, you are being asked to complete the following recursive function: V search(bstnode<e,v> T, E key) throws NotFoundException { V search(bstnode<e,v> T, E key) throws NotFoundException { if (T == null) throw new NotFoundException(); else if (key.compareto(t.key) == 0) return T.value; else if (key.compareto(t.key) < 0) return search(t.left, key); else return search(t.right, key); } 7 of 15
(2.2) (3 marks) Let T(h) denote the number of steps used to search in a binary search tree of height h in the worst case. Then there are positive constants c 1 and c 2 such that c 1 if h = 1 T(h) (1) c 2 + T(h 1) if h 0 Use this relation to prove (via mathematical induction) that T(h) c 2 ( h + 1 ) + c 1 (2) (a) Base Case: h = 1 = T( 1) c 1 (b) Inductive Step: assume that for h = 1,, k. Prove that T(h) c 2 ( h + 1 ) + c 1 T(k + 1) c 2 ( k + 2 ) + c 1 Since k + 1 0, using (1), we have T(k + 1) c 2 + T(k) Using (2) for T(k), it is obtained that This completes the proof. T(k + 1) c 2 + T(k) c 2 + c 2 (k + 1) + c 1 = c 2 (k + 2) + c 1 8 of 15
QUESTION 3 (16 marks) The following questions deal with sorting algorithms. (3.1) (5 marks) Consider the merge operation used in mergesort. Give an algorithm using pseudocode that merges two ordered linked lists L 1 and L 2 into one ordered linked list L and returns the combined list. Your algorithm must run in time O(n). The following definitions are assumed: public class List<E> { } public bool isempty(); // returns true if empty; otherwise false public E head(); // returns the head element, but does not remove it public void insert(e e); // inserts `e' at the tail of the list public E delete(); // returns and removes the head element where, you are being asked to complete the following recursive function: List<E> merge(list<e> L 1, List<E> L 2 ) { List<E> merge(list<e> L1, List<E> L2) { L = create an empty list; while (!L1.isEmpty() and!l2.isempty()) { if (L1.head() <= L2.head()) L.insert(L1.delete()); else L.insert(L2.delete()); } while (!L1.isEmpty()) L.insert(L1.delete()); } while (!L2.isEmpty()) L.insert(L2.delete()); 9 of 15
(3.2) (4 marks) Using asymptotic notation, describe the running time of the following algorithms to sort an array of length n. Provide answers for two cases: (i) the input array is sorted in the correct order ( sorted array ), and (ii) the input array contains n copies of the same value ( duplicate array ). sorting algorithm sorted array duplicate array insertionsort Θ(n) Θ(n) mergesort Θ(n log n) Θ(n log n) heapsort Θ(n log n) Θ(n) quicksort Θ(n 2 ) Θ(n 2 ) (3.3) (7 marks) Consider the deterministic partitioning operation used in deterministic quick sort. We modify the partitioning algorithm so that it always partition an input array of length n to two partitions in such a way that the length of the left partition is n K and the length of the right partition is K 1 (for some constant K > 0). Let us refer to this partitioning algorithm as KPartition. Next, consider the following variation of the quick sort algorithm called KQuickSort: KQuickSort(int[] A, int low, int high) n = high low + 1 if n < K then insertionsort(a, low, high) else q = KPartition(A, low, high) KQuickSort(A, low, q-1) // sort the left partition of length n K insertionsort(a, q+1, high) // sort the right partition of length K 1 end if (a) (3 marks) Let T(n) denote the worst-case number of steps to run KQuickSort on input arrays of size n. Complete the following recurrence relation for T(n). Assume that KPartition takes Θ(n) steps to partition an array of size n. c 1 n < K, T(n) c 2 + c 3 n + T(n K) n K for some constants c 1, c 2, c 3 > 0. 10 of 15
(b) (2 marks) Use the substitution technique to find an upper bound on T(n). For simplicity, assume that n is a multiple of K, i.e., n = m K (m 0 is an integer). You may find the following relation useful: n + (n K) + (n 2K) + + K = m (n + K) 2 For n K: T(n) c 2 + c 3 n + T(n K). c 2 + c 3 n + [c 2 + c 3 (n K) + T(n 2K)] = 2c 2 + c 3 [n + (n K)] + T(n 2K) 2c 2 + c 3 [n + (n K)] + [c 2 + c 3 (n 2K) + T(n 3K)] = 3C 2 + c 3 [n + (n K) + (n 2K)] + T(n 3K) mc 2 + c 3 [n + (n K) + (n 2K) + + K] + T(0) = mc 2 + c 3 1 2 m(n + K) + c 1 = c 2 K n + c 3 2K n(n + K) + c 1 (c) (1 mark) Using the Big-O notation, give an asymptotic expression for the worst-case running time of KQuickSort (no proof is required). O(n 2 ) (d) (1 mark) How does the worst-case asymptotic running time of KQuickSort compares with that of quicksort? Both have the same worst-case running time O(n 2 ). 11 of 15
QUESTION 4 (12 marks) The following questions deal with graphs and their algorithms. (4.1) (4 marks) Recall that the depth-first search algorithm presented in class assumes an adjacencylist representation of the input graph. We modify the algorithm to work with an adjacencymatrix representation of the input graph. The modified algorithm is presented below in which the for loop in DFS-Vist has been modified to iterate over the adjacency matrix Adj. The rest of the algorithm remains intact. DFS(G = (V, E), s) 1: for each vertex u V do 2: colour[u] = white 3: π[u] = NIL 4: end for 5: DFS-Visit(s) 6: return π DFS-Visit(u) 1: colour[u] = grey 2: for each vertex v V do 3: if Adj[u, v] == 1 and colour[v] == white then 4: π[v] = u 5: DFS-Visit(v) 6: end if 7: end for 8: colour[u] = black (a) (2 marks) What is the worst-case asymptotic running time of the modified DFS algorithm on a graph G = (V, E)? Briefly justify your answer. i. initialization: Θ( V ) ii. no. of calls to DFS-Visit: Θ( V ) iii. no. of steps in each call of DFS-Visit: Θ( V ) Therefore, the total running time is in Θ( V 2 ) (b) (2 marks) How does the worst-case asymptotic running time of the modified DFS compare with that of the unmodified DFS presented in class? Briefly explain. The worst-case running time of unmodified algorithm is in Θ( V + E ). In general, E < V 2. Thus, the modified algorithm is always slower. Specifically, for dense graphs where E = O( V 2 ), both algorithms have the same asymptotic running time. However, for sparse graphs where E = O( V ), the unmodified version is asymptotically faster. 12 of 15
(4.2) (4 marks) Consider the following directed graph and assume that the neighbors of any vertex are always accessed in their respective alphabetical order by label. b e a d g c f (a) (2 marks) Give the final contents of the array π (i.e., the predecessor function) after running depth-first search on this graph, using vertex a as the source. Draw the resulting depth-first tree. b e a d g a b c d e f g π a f e b e f c f (b) (2 marks) Give the final contents of the array d (i.e., the distance function) after running breadth-first search on this graph, using vertex a as the source. Draw the resulting breadth-first tree. b e a d g a b c d e f g d 0 1 1 2 2 3 3 c f 13 of 15
(4.3) (4 marks) Consider the following weighted undirected graph and assume that the neighbors of any vertex are always accessed in their respective alphabetical order by label. 3 a 2 c b 2 1 1 f e 3 d 4 (a) (2 marks) Draw the shortest-paths tree produced by running the Dijkstra s algorithm on this graph, using vertex a as the source. 3 a 2 c b 1 1 f e 3 d (b) (2 marks) Draw the minimum-cost spanning tree produced by running the Prim s algorithm on this graph, using vertex a as the source. a 2 c b 2 1 1 f e 3 d 14 of 15
extra page 15 of 15