CS140 Midterm 2

Fall 2016

Instructions

  1. The exam has 11 problems worth 110 points and you will be graded out of 110 points. For example 100/110 will be scored as 91%.

  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. (14 points) For each of the following questions circle the best answer from the above list. 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 what I said about them in class. Then 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.
     
         a. vector
         b. list
         c. deque
         d. map
         e. set
         f. multimap
         g. multiset
         h. hash table
    

  2. (11 points) Suppose I have the following declarations and code:
         int a, b, c;
         int *p, *q, *r;
    
    Also suppose that the above variables are assigned the following memory addresses:
         a: 0x1000
         b: 0x1004
         c: 0x1008
         p: 0x100c
         q: 0x1010
         r: 0x1014
     
    What values are printed by the following two cout statements. Note that the first cout statement prints p and q whereas the second cout statement prints *p and *q.
           p = &c;
           *p = 20;
           p = &a;
           *p = 40;
           q = p;
           b = *q + *p;
           cout << a << " " << b << " " << c << " " << p << " " << q << endl;
    
           a = 60;
           b = 20;
           c = 70;							    
           r = &b;
           b = a + c;		    
           p = new int;
           *p = 2;
           q = new int;
           *q = a / *p;
           cout << a << " " << b << " " << c << " " << *p << " " << *q << " " << *r << endl;
    
    cout 1:                      cout2:
    
         a:                      a:
    
    
         b:                      b:
    
    
         c:                      c:
    
    
         p:                     *p:
    
    
         q:                     *q:
    
    
                                *r:
    
    

  3. (6 points) For each of the following definitions, select the term from the following list that best fits that definition.
     a. memory leak           b. dangling pointer     c. seg fault
        
    1. __________________ Occurs when you try to de-reference a NULL pointer (e.g., p = NULL; *p = 20).

    2. __________________ Occurs when you forget to delete a heap-allocated object when there are no longer any pointers to that object (i.e., you can no longer reference that object).

    3. ___________________ Occurs when you pre-maturely de-allocate a heap-allocated object when there are still pointers to that object (i.e., when parts of the program may still try to reference that object).

  4. (10 points) Explain what is inefficient about the following code segment and how you would fix it. The code segment is supposed to use linear probing to find a key in a hash table. It should return the index of the key if it is found in the hash table and -1 if the key is not in the hash table. The keys are guaranteed to be non-negative integers. A hash table entry stores one of three values: 1) a key, 2) a value of -1 to denote that the entry has never stored a key, and 3) a value of 0 to denote that the entry is currently empty but once stored a key. A sample hash table might be:

    012345678910
    -1122304835-1-130-132

    int find(vector<int> &hashTable, int key) {
        unsigned int bucket = key % hashTable.size();
        int i;
    
    1    for (i = bucket; i != bucket-1; i = (i+1) % hashTable.size()) {
    2        if (hashTable[i] == key) {
    3            return i;
    4        }
    5    }
    6    return -1;
    }
    
    1. In four sentences or less, describe why the above find function is inefficient (i.e., although this code returns the correct result, it does not faithfully implement the find algorithm for linear probing).
      
      
      
      
      
            
    2. Fix the inefficiency by writing an appropriate code segment and indicate where you would insert it (i.e., after which line number).
      
      
          

  5. (9 points) Your boss has asked you to choose between two algorithms. The two algorithms have the following running times:
    	  Algorithm 1: T(n) = 1000n*log2n + 20n + 3
    	  Algorithm 2: T(n) = 0.5n2 + 50n + 8
            

    1. What is the Big-O running time of Algorithm 1?

    2. What is the Big-O running time of Algorithm 2?

    3. The size of the input will be typically quite large, on the order of n > 1000. Which of the two algorithms should you choose and why?

  6. (10 points) You are told that the running time of an O(n) algorithm for a certain value of n is 8 seconds.

    1. How long in seconds would you expect an O(log2 n) algorithm to take for the same value of n? If you forget how to compute log2n, it is the value of x, where 2x = n.
    2. How long in seconds would you expect an O(n2) algorithm to take for the same value of n?

  7. (10 points) In the Coke lab, one of your functions had to delete a user. One of the tasks this function had to perform was to remove all of the user's phones from the Phone map. To refresh your memory, here are the relevant data structures:
    class User {
      public:
         string username;
         string realname;
         int points;
         set <string> phone_numbers;
    };
    
    map <string, User *> Phones;
    
    Here are two code segments that correctly remove all of the user's phones from the Phones map:
    (a)(b)
    void deletePhones(User *u) {
       set::iterator phone_it;
       for (phone_it = u->phone_numbers.begin();
             phone_it != u->phone_numbers.end();
             phone_it++) {
          Phones.erase(Phones.find(*phone_it));
       }
    }	
    
    void deletePhones(User *u) {
       map<string, User*>::iterator phone_it;
    
       // phone_it is correctly advanced in the loop body, which is
       // why there is no phone_it++ as the third expression in the for header
       for (phone_it = Phones.begin();
             phone_it != Phones.end(); ) {
    
          if (phone_it->second == u) { 
             phone_it = Phones.erase(phone_it);
          }                                   
          else {                        
             phone_it++;
          }
      }   
    } 
    

    Make the following assumptions:

    Answer the following questions:

    1. Which code segment is more efficient (a or b)?

    2. Explain why the code segment you selected is more efficient using Big-O notation. You will need to compute the Big-O running time of each of the two functions in order to justify your answer.

  8. (10 points) Draw a list diagram that shows what the following list looks like pictorially after the following code executes. Make sure that you include sentinel nodes and that you show where each list iterator points. Your diagram should look like the ones in the class notes.
    list<int> names;
    list<int>::iterator nit, nit1;
    names.push_back(45);
    names.push_back(100);
    names.push_front(80);
    names.push_front(50);
    names.push_front(200);
    nit = names.begin();
    nit++;
    nit++;
    nit1 = nit;
    nit1++;
    names.erase(nit);
    nit = nit1;
    nit++;
    names.insert(nit, 15);
    nit1 = names.end();
    
    
  9. (10 points) Assume you have the following declaration:
    class ListNode {
       public:
          string name;
          ListNode *next;
          ListNode *prev;
    
          ListNode(string n) : name(n) {}
    };
    
    Further suppose that a series of inserts have created the following list:

    1. Draw the diagram that results when the following code is executed:
      ListNode *newnode;
      
      1) newnode = new ListNode("Brad");
      2) newnode->next = currentNode;
      3) newnode->prev = currentNode->prev;
      
      4) currentNode->prev = newnode;
      5) currentNode->prev->next = newnode;
      
      Warning: There is a flaw in this code and so you should not automatically draw the diagram you think will appear. In fact, you should draw the nodes in the region of the new node on a separate piece of paper and only when you are sure of the solution, copy your solution to the exam. Otherwise, if you are using ink, you will find that you have to scratch out your answer and redo it.
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
      
       
    2. How could you modify this code so that is correctly inserts newnode into the list? Be specific by identifying a) which line(s) of code are causing the problem, and b) how you would modify these lines (write the modification as C++ code).