











Study with the several resources on Docsity
Earn points by helping other students or get them with a premium plan
Prepare for your exams
Study with the several resources on Docsity
Earn points to download
Earn points by helping other students or get them with a premium plan
Solutions to the final exam of CS61BL: Data Structures & Programming Methodology Summer 2014. The exam covers topics such as sorting algorithms, big-Oh running time, and assumptions made about the implementation of sorting algorithms. explanations and runtimes for sorting algorithms such as Quicksort, merge sort, heap sort, tree sort, and counting sort. It also includes explanations for optimized bubble sort and insertion sort. useful for students studying data structures and programming methodology.
Typology: Exams
1 / 19
This page cannot be seen from the preview
Don't miss anything!












Let n be some large integer, k < log 2 n. For each of the input arrays described below, explain which sorting algorithm you would use to sort the input array the fastest and why you chose this sorting algorithm. Make sure to state any assumptions you make about the implementation of your chosen sorting algorithm. Also specify the big-Oh running time for each of your algorithms in terms of k and n. Your big-Oh bounds should be simplified and as tight as possible.
(a) Input: An array of n Comparable objects in completely random order
Sorts : Quicksort, merge sort, heap sort, tree sort Runtime : O(n log n) Explanation : Merge sort and heap sort are always O(n log n). For quicksort, we can easily choose a good pivot for randomly ordered inputs. For tree sort, the resulting tree will be fairly balanced in the average case. Comments: You needed to use a comparison-based sort because input contains Comparable objects. Bubble, insertion, and insertion sort are inefficient compared to the four listed above. Runtime should not include k.
(b) Input: An array of n Comparable objects that is sorted except for k randomly located elements that are out of place (that is, the list without these k elements would be completely sorted) Sort : Insertion sort Runtime : O(nk) Explanation : For the n − k sorted elements, insertion sort only needs 1 comparison to check that it is in the correct location (larger than the last element in the sorted section). The re- maining k out-of-place elements could be located anywhere in the sorted section. In the worst case, they would be inserted at the beginning of the sorted section, which means there are O(n) comparisons in the worst-case for these k elements. This leads to an overall runtime of O(nk + n), which simplifies to O(nk). Comments: It was a common error to say the runtime was O(n) or O(n^2 ). We gave partial credit for both of these answers. O(n) was incorrect because it underestimated the number of comparisons required for the out-of-place elements. O(n^2 ) was incorrect because it ignored the fact that only 1 comparison is needed for already sorted elements. Also, it is incorrect to equate k with log 2 n. It was only given that k < log 2 n.
(c) Input: An array of n Comparable objects that is sorted except for k randomly located pairs of adjacent elements that have been swapped (each element is part of at most one pair). Sort Option 1 : Bubble sort with optimization Runtime 1 : O(n) or O(n + k) Explanation 1 : It was necessary to mention the optimization for bubble sort, which involves stopping after a complete iteration of no swaps occurring. In order words, optimized bubble sort stops once the list is sorted, not after it has run through n iterations for a runtime of O(n^2 ).
CS61BL, Summer 2014, Final Exam Solutions 1
It takes one iteration of bubble sort to swap the k randomly reversed pairs of adjacent elements then another iteration before optimized bubble sort stops due to no swaps. If you count each swap as taking O( 1 ) time, the total runtime is O( 2 n + k), which simplifies to O(n). Sort Option 2 : Insertion sort Runtime 2 : O(n) or O(n + k) Explanation 2 : Insertion sort requires 1 comparison for the n−k sorted elements then requires 2 comparisons for the second element in each of the k pairs. This leads to a runtime of O(n+k), which simplifies to O(n).
(d) Input: An array of n elements where all of the elements are random ints between 0 and k
Sort Option 1 : Counting sort Runtime 1 : O(n) or O(n + k) Explanation 1 : Counting sort involves initializing an array of size k, then going through n elements while incrementing numbers in the array. Recovering the sorted list requires going through k buckets and outputting n numbers. This is a total runtime of O( 2 n + 2 k), which simplifies to O(n + k) or O(n). Comments: Counting sort was the easier sort to explain. Radix sort, explained below, re- quired a more intricate explanation to prove that it had as efficient a runtime as counting sort. Sort Option 2 : Radix sort Runtime 2 : O(n) Explanation 2 : Radix sort is O((n + b)d), where b is the number of buckets used and d is the number of digits representing the largest element. (In this case, the largest element was k.) Because the input array is composed of Java ints, we can say that b is equal to 2 and d is equal to 32 because Java ints are 32-bits long, and each bit can be a 0 or 1. Thus, because b and d are constants, the runtime for radix sort on Java ints is O(n). Comments: We also accepted the answer that b was 10 and d was a constant when simplify- ing the runtime to O(n) based on Java ints. While it is true that radix sort runs in O(n log k) because the number k can be represented in log k bits and b would then be a constant, this runtime did not prove that radix sort was as efficient of a sort as counting sort. Thus, we only gave partial credit for this runtime. Sort Option 3 : Bucket sort Runtime 3 : O(n) or O(n + k) Explanation 3 : The correctness of bucket sort as an answer depended heavily on the choice of buckets (and how many buckets were used, in particular). Solutions that chose k buckets were quite similar to counting sort and thus had the same runtime as counting sort. Comments: Solutions that did not explain the choice or number of buckets were docked points because bucket sort actually refers to any sorting algorithm that involves placing elements in buckets. Thus, counting sort, radix sort, and even quicksort and merge sort are all categorized as bucket sorts. Only explanations of bucket sort that were specific about the implementation received full points.
For each part below, assume that nodes are ordered alphabetically by their letter (i.e. the value of "A" is less than the value of "B", which is less than the value of "C", etc...)
(a) Draw the splay tree that results after calling find("A") on the splay tree below.
Solution:
E
Rotate B and C right
Rotate A and B right
Rotate D and E right
Rotate A and D right
(b) Add a "B" node to the AVL tree below by drawing it below. Then draw the tree that results after the AVL tree balances itself. Show all intermediary steps, if any. A
Solution:
A
Rotate B and C right
Rotate B and A left
(c) Draw the 2-3 tree that results after inserting the following elements in the given order:
1 2 3 4 5 6 Solution:
1 Add 2
1 2 Add 3
Bump 3 node up
Add 4 to right leaf
Add 5 to right leaf
Bump 4 node up, causing 3 node to become mid- dle child
Add 6 node to rightmost leaf
(d) Draw the 2-3 tree that results after inserting the following elements in the given order:
4 2 1 3 6 5 Solution:
4 Add 2
2 4 Add 1
Bump 2 node up
Add 3 to right leaf
Add 6 to right leaf
Bump 4 node up, causing 3 node to become mid- dle child
Add 5 node to rightmost leaf
5 Pair (5 points)
Write a program, Pair.java, that generates the box-and-pointer diagram shown below when run. Your program should include a class with a main method. In the diagram below, each object’s static type is labeled next to the corresponding variable name. Each object’s dynamic type is not shown.
Solution:
public class Pair<T1, T2> { T1 o1; T2 o2;
public static void main(String[] args) { Pair<Integer, String> p1 = new Pair<Integer, String>(); Pair<Pair, Pair> p2 = new Pair<Pair, Pair>(); p1.o1 = new Integer(7); p1.o2 = "Okay!"; p2.o1 = p2; p2.o2 = p1; } }
Comments: Many students didn’t use generics, and instead made instance variables have static type Object (we only took off one point for this). Another common mistake was to use p2 before it was initialized (for example, Pair p2 = new Pair(p1, p2);)
6 Poorly Named Variables (6 points)
1 public class V { 2 p r i v a t e i n t VVVVV ; 3 4 public V( i n t VVVV) { 5 VVVVV = VVVV ; 6 } 7 8 p r i v a t e i n t VV ( ) { 9 r et ur n 0 ; 10 } 11 12 public double OOOO( ) { 13 r et ur n 0 ; 14 } 15 }
1 public class O extends V { 2 p r i v a t e i n t OOO; 3 }
For each of the following methods, explain whether the O class would still compile if the method is added to it. If the O class wouldn’t compile, explain why. The O class may have additional methods / constructors. The V class does not.
(a) public O() { } This would not compile. In a constructor, if there is no explicit call given to a super constructor, an implicit one is made. Since the implicit call would have been super() and class V does not have a no-arg constructor, you will get a compile-time error. Comments: A common error was assuming V had a no-arg constructor. However, the default no-arg constructor ceases to exist once you define an explicit constructor.
(b) void OO() { OOO = VV(); } This would not compile. VV is a private method and cannot be called in any class outside of V. Comments: Some people thought the error was the lack of a privacy keyword (i.e. public, private, protected). However, lack of a privacy keyword simply means the method has default privacy protections. default protection means that the method or variable can only be accessed by code within the same class or package.
(c) public boolean OOOO() { return false; } This would not compile. Since the method signature was the same (i.e. same name and same parameters) but the return types were different, Java would be unable to differentiate between the two. One could have defended that this was either a failed override or a failed overload depending on how you explained your answer. Comments: Many people were confused on the terminology of method signature and method header. A method’s signature includes its name and parameters. A method’s header includes the privacy modifier and return type in addition to the method signature.
(c) With our changes above, what is the new big-Oh running time of Dijkstra’s algorithm (in terms of |V | and |E|)? Show your work.
Solution: |V | ∗ 1 + |V | ∗ |V | + |V | ∗ 1 + |E| ∗ 1 + |E| ∗ 1 = 2 ∗ |V | + |V |^2 + 2 ∗ |E| ∈ O(|V |^2 + |E|) ∈ O(|V |^2 ) Multiplying corresponding answers from parts (a) and (b) and summing them gives us an upper bound on the running time. In this case, this is also a tight bound since dequeue takes time proportional to the number of keys in the HashMap. The first time we call dequeue there are |V | keys, then |V | − 1 keys, and so on. This gives up a running time of O(|V |^2 ). Finally, since |E| ≤ |V |^2 for any graph, O(|V |^2 + |E|) ∈ O(|V |^2 ).
8 Buggy Priority Queue (10 points)
Joe Cool is implementing a priority queue with a binary min-heap using an array list (his code is provided below). However, his pair programming partner tells him that, with this implementation, calls to dequeue will not always work as intended.
1 import j a v a. u t i l. A r r a y L i s t ; 2 3 public class MyPriorityQueue { 4 5 p r i v a t e A r r a y L i s t < I n t e g e r > binMinHeap ; 6 7 public MyPriorityQueue ( ) { 8 binMinHeap = new A r r a y L i s t < I n t e g e r > ( ) ; 9 binMinHeap. add ( n u l l ) ; 10 } 11 12 / / Removes and r e t u r n s i t e m i n p r i o r i t y queue w i t h s m a l l e s t p r i o r i t y 13 public I n t e g e r dequeue ( ) { 14 I n t e g e r t o R e t u r n = binMinHeap. g et ( 1 ) ; 15 binMinHeap. s e t ( 1 , binMinHeap. remove ( binMinHeap. s i z e ( ) − 1 ) ) ; 16 bubbleDown ( 1 ) ; 17 r et ur n t o R e t u r n ; 18 } 19 20 / / Adds i t e m t o p r i o r i t y queue 21 public void enqueue ( I n t e g e r i t em ) { 22 binMinHeap. add ( i t e m ) ; 23 bubbleUp ( binMinHeap. s i z e ( ) − 1 ) ; 24 } 25 26 / / Swaps t h e elements a t index1 and index2 o f t he b i n a r y min heap 27 p r i v a t e void swap ( i n t index1 , i n t index2 ) { 28 i n t temp = binMinHeap. g e t ( index1 ) ; 29 binMinHeap. s e t ( index1 , binMinHeap. g et ( index2 ) ) ; 30 binMinHeap. s e t ( index2 , temp ) ; 31 } 32 33 / / Bubbles up t h e element i n t he b i n a r y min heap a r r a y l i s t a t given index 34 p r i v a t e void bubbleUp ( i n t i nd e x ) { 35 while ( i n de x / 2 > 0 && binMinHeap. g et ( index ) < binMinHeap. g et ( index / 2 ) ) { 36 swap ( index , i n de x / 2 ) ; 37 i n de x = i n de x / 2 ; 38 } 39 } 40 41 / / Bubbles down t h e element i n t he b i n a r y min heap a r r a y l i s t a t given index 42 p r i v a t e void bubbleDown ( i n t i ndex ) { 43 i n t n = binMinHeap. s i z e ( ) ; 44 while ( i n de x ∗ 2 < n && binMinHeap. ge t ( index ) > binMinHeap. ge t ( index ∗ 2 ) ) { 45 swap ( index , i n de x ∗ 2 ) ; 46 i n de x = i n de x ∗ 2 ; 47 } 48 } 49 }
Solution: Example 2
if (index >= n || index * 2 >= n) { return; } int curr = binMinHeap.get(index); int left = binMinHeap.get(index * 2); if (index * 2 + 1 == n) { // no right children if (curr > left) { swap(index, index * 2); bubbleDown(index * 2); } } else { // two children int right = binMinHeap.get(index * 2 + 1); if (left < right) { if (curr > left) { swap(index, index * 2); bubbleDown(index (^) * 2); } } else { if (curr > right) { swap(index, index * 2 + 1); bubbleDown(index (^) * 2 + 1); } } }
Comments: The following are common errors:
9 Every Other (8 points)
1 public class M y L i n k e d L i s t { 2 3 p r i v a t e ListNode head ; 4 5 public M y L i n k e d L i s t ( ListNode inputHead ) { 6 head = inputHead ; 7 } 8 9 public M y L i n k e d L i s t ( Obj ec t i t e m ) { 10 head = new ListNode ( i t e m ) ; 11 } 12 13 p r i v a t e class ListNode { 14 p r i v a t e Ob je ct i t e m ; 15 p r i v a t e ListNode n e x t ; 16 17 public ListNode ( Obj ec t i n p u t I t e m ) { 18 t h i s ( i n p u t I t e m , n u l l ) ; 19 } 20 21 public ListNode ( Obj ec t i n p u t I t e m , ListNode next ) { 22 t h i s. i t e m = i n p u t I t e m ; 23 t h i s. n e x t = n e x t ; 24 } 25 / / There may be o t h e r methods n ot shown here 26 } 27 / / There may be o t h e r methods n ot shown here 28 }
On the next page , write an evenOdd method in the MyLinkedList class above that destructively sets the linked list to contain every other linked list node of the original linked list, starting with the first node. Your method must also return a linked list that contains every other linked list node of the original linked list, starting with the second node.
Your method should work destructively and should not create any new ListNode objects. If a MyLinkedList contains zero elements or only one element, a call to evenOdd should return null. The last ListNode of each MyLinkedList has it’s next instance variable set to null.
Example: If a MyLinkedList initially contains the elements [5, 2, 3, 1, 4], then a call to evenOdd should return a MyLinkedList with the elements [2, 1], and after the call, the original MyLinkedList should contain the elements [5, 3, 4]
10 MemoryMap (12 points)
You have been tasked with designing a MemoryMap class that implements the Map<K, V> interface and supports the following operations:
Example:
MemoryMap<String, String> map = new MemoryMap<String, String>(); map.put("A", "1"); map.put("B", "2"); map.recent(2); // Returned list contains: ["B", "A"] map.get("A"); // Returns "1" map.recent(2); // Returned list contains: ["A", "B"] map.put("C", "3"); map.recent(3); // Returned list contains: ["C", "A", "B"] map.remove("A"); map.recent(2); // Returned list contains: ["C", "B"]
(a) Explain in words how you would implement MemoryMap (including which data structures you would use) so that the operations listed on the previous page are time efficient. Space effi- ciency is not a concern. Do not write any code. Solutions that are as efficient as possible will receive full credit. Less efficient solutions may receive partial credit. Solution: For part (a), multiple solutions were accepted as long as the student demonstrated usage of data structures that could potentially solve the MemoryMap question (without con- sideration for efficiency). Some examples of accepted solutions were:
Comments: Some common errors were:
Reference Sheet: ArrayList
Here are some methods and descriptions from Java’s ArrayList
Return type and signature Method description
boolean add(E e) Append the specified element to the end of the list
boolean contains(Object o) Returns true if this list contains the specified element
E get(int index) Returns the element at the specified position in this list
Iterator
E remove(int index) Removes the element at the specified position in this list
boolean remove(Object o) Removes the first occurrence of the specified element from this list, if it is present
E set(int index, E element)
Replaces the element at the specified position in this list with the specified element (returns the previous element at this position)
int size() Returns the number of elements in this list
Reference Sheet: Map<K, V>
Here are some methods and descriptions from Java’s Map<K, V> interface API.
Return type and signature Method description
V get(Object key) Returns the value to which the specified key is mapped, or null if this map contains no mapping for the key
boolean isEmpty() Returns true if this map contains no key-value mappings
V put(K key, V value)
Associates the specified value with the specified key in this map (returns the previous value associated with key, or null if there was no mapping for key)
V remove(Object key) Removes the mapping for a key from this map if it is present
Set