CS140 Final

Fall 2018

Instructions

  1. This paper exam has 8 problems worth 90 points and and the coding portion was worth 60 points, for a total of 150 points. You will be graded out of 150 points, so if your total combined score is 135, you will receive a 90 for the exam.

  2. Write your answers on the exam.

  3. Ensure that you write clearly and legibly. Failure to do so may result in the deduction of points.

  4. No electronic devices are allowed and all electronic devices that you have with you must be turned off, including calculators.

  5. You may not use any notes for this exam.

  6. Good luck!

  1. (10 points) Show the binary search tree that results when the keys are presented in the following order:
    	300 150 40 80 450 600 550 200 800 20
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
    
  2. (10 points) Show the binary search tree that results if susan is deleted from the tree below:
                    ------nancy----------------
                   /                           \
                -fred-                     ---susan-------
                /     \                   /               \
            bonnie  george             peter            zachary
                   /      \                \  	   /     
                charles  nick              sarah     yifan
                                          /          /
    	                          rebecca      xavier
                                     /
                                  ralph
    

  3. (10 points) Show the result of doing a single left rotation about the node 500. Do not worry if the rotation increases the height of the tree. All I care about is whether you know how to perform a rotation.
                                -300-
                               /     \
                             175	 500
                            /       /   \
    		      100     400  600
    		      /      /   \
    		     50	   350   450
    
  4. (12 points) If we delete 60 from the following AVL tree
                         --100---
                        /        \
                       40        200
                         \      /   \
                         60   150   400
                             /  \
                           125  175
      
    we end up with the following binary search tree that is not a valid AVL tree and hence needs to be re-balanced:
                         --100---
                        /        \
                       40        200
                                /   \
                              150   400
                             /  \
                           125  175
      

  5. (10 points) Behold the following recursive function that computes and returns the sum of an array of n numbers:
    int sum(int numbers[], int start, int end) {
    1)   int middle = (start+end)/2;
    2)   return sum(numbers, start, middle) + sum(numbers, middle + 1, end);
    }
      
    Answer the following questions about this function:

    1. Why is this function incorrect? You must answer in 3 sentences or less.
      
      
      
      
      
      
      
      	
    2. Write the C++ code fragment that you must add to the above function to make it compute the sum correctly and indicate the line that it should be placed before or after (e.g., you might write after line x where x is the line number). You may not modify any code from the above function.
      
      
      
      
      
      
      
      	

  6. (15 points) The greatest common divisor (gcd) of two non-zero numbers is the largest positive integer that divides the numbers with a remainder of 0. For example, the gcd of 48 and 20 is 4, the gcd of 48 and 12 is 12, and the gcd of 12 and 12 is 12.

    Euclid's algorithm is a recursive algorithm for finding the gcd of two integers a and b that can be written as the following C++ function:

    int gcd(int a, int b) {
    1)    if (a == b) return a;
    2)    else if (a < b) return gcd(a, b-a);
    3)    else return gcd(a-b, b);
    }	
    Suppose I have the following main function:
    int main() {
    1)    int x = 48;
    2)    int y = 20;
    3)    cout << gcd(x, y) << endl;
    }
    
    In the stack diagram shown below, complete the stack frames that exist when the series of recursive calls finally arrives at the base case for gcd.

    	  |----------------------------------------|
    	  | main:                                  |
    	  |    line: 3                             |
    	  |    x: 48                               |
    	  |    y: 20                               |
    	  |----------------------------------------|
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |----------------------------------------|
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |----------------------------------------|
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |----------------------------------------|
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |----------------------------------------|
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |----------------------------------------|
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |                                        |
    	  |----------------------------------------|
    	
  7. (12 points) Behold the following 4 fragments of code:
    (a)
    int i, j;
    int sum = 0;
    for (i = 0; i < n*n; i++) 
        sum += i;
    for (j = 0; j < n/2; j++)
        sum *= j;
         
    (b)
    for (year = 0; year < 2000; year++) {
        for (day = 0; day < 365; day++) {			
            if (n == year % day) {
                printf("year = %d and day = %d\n");			
            }
        }
    }			
    			
    (c)
    int mystery(vector<int> &row, vector<int> &col) {
      int i;
      int result = 0;
    
      for (i = 1; i < row.size(); i *= 2) {
        result += row[i] * col[i];
      }
      return result;
    }
    
    (d)
    In the following code, assume that the function f is O(n2)
    int i, j;
    int sum_f = 0, sum_loops = 0;
    for (i = 0; i < n; i++) {
        sum += f(i);
    }	    
    for (i = 0; i < n; i++) {
      for (j = i; j < n; j++) {				
         sum += i * j;
      }  
    }
    if (sum_f > sum_loops) 
        cout << sum_f << endl;		  
    else
        cout << sum_loops << endl;		     
    			 

    For each fragment of code, please circle its Big-O running time:

    a      O(1)    O(log n)   O(n)   O(n log n)   O(n2)  O(n3)   O(2n)
    
    b      O(1)    O(log n)   O(n)   O(n log n)   O(n2)  O(n3)   O(2n)
    
    c      O(1)    O(log n)   O(n)   O(n log n)   O(n2)  O(n3)   O(2n)
    
    d      O(1)    O(log n)   O(n)   O(n log n)   O(n2)  O(n3)   O(2n)
    

  8. (12 points)
     
         a. array
         b. vector
         c. stack
         d. deque
         e. hash table
         f. list
         g. binary search tree
         h. AVL tree
    

    For each of the following questions choose the best answer from the above list. Assume that the size of an array is fixed once it is created, and that its size cannot be changed thereafter. Sometimes it may seem as though two or more choices would be equally good. In those cases think about the operations that the data structures support and choose the data structure whose operations are best suited for the problem. You may have to use the same answer for more than one question:

    1. ____________ The data structure you should use if you want to implement a map that records the results of a 10K race and the keys are the last names of the runners. The keys must be kept in alphabetical order and the runners names will be entered in the order that the runners finish the race (hence if "Joe" finishes before "Barry" then "Joe" will be inserted first and then "Barry").

    2. ____________ The data structure you should use if you want to implement a map that records the results of a 10K race and the keys are the race times of the runner. The keys must be kept in sorted order and the race times will be entered in the order that the runners finish the race (hence a runner with the time 15:21 will be inserted before a runner with the time 15:36).

    3. ____________ The data structure you should use if you want to reverse a file by reading its lines, adding each line to the front of the data structure, and then traversing the data structure from front to back and printing the lines.

    4. ____________ The data structure you should use to store a collection of emails where the emails are ordered by the time that they arrived and they are inserted at the time that they arrived. You want to be able to print the emails in sorted order by time of arrival, insert/delete emails, and find emails.

    5. ____________ The data structure that is used to implement hashing with linear probing when the size of the data is known in advance (the answer is not a hash table--I want to know the data structure used to implement the hash table).

    6. ____________ The best data structure to use to implement the buckets in separate chaining. Each bucket holds the key/value pairs that hash to that bucket and new key/value pairs are typically added to the end of the bucket.