CS140 Midterm 2 Solutions

Spring 2018

  1. (14 points) For each of the following questions circle the best answer from the above list.
     
         a. vector
         b. list
         c. deque
         d. map
         e. set
         f. multimap
         g. multiset
         h. hash table
    

  2. (8 points) Suppose I have the following declarations and code:
         int a, b;
         int *x, *y;
    
    Also suppose that the above variables are assigned the following memory addresses:
         a: 0x1000
         b: 0x1004
         x: 0x1008
         y: 0x100c
     
    After the following code sequence executes, what are the values of a, b, x, and y?
           x = &b;
           y = x;
           a = 10;
           b = 40;
           *x = 30;
           *y = *x * 3;
    
         a: 10
         b: 90
         x: 0x1004
         y: 0x1004
    
    

  3. (6 points) Memory Leak What term is used to describe the problem with the following code? If you can't think of the term, then for 3 points give a 2 sentence or less answer that describes the problem.
      int *x;
      x = new int;
      *x = 40;
      x = new int;
      *x = 60;
    
    The code fails to delete the original heap-allocated piece of memory to which x pointed, and as a result, that piece of memory never gets deleted even though it no longer is accessable because it has no pointers to it.

  4. (10 points) Suppose I have the following declarations:
      class PersonNode {
      public:
        string name;
        PersonNode *next;
        PersonNode(string n) : name(n), next(NULL) {}
      }
    
      PersonNode *x;
      PersonNode *y;
      PersonNode *newnode;
    
    1. Draw the diagram that results when the following code is executed. Your diagram should look like the ones for linked structures in the class notes and should include the three pointer variables x, y, and newnode, plus any PersonNodes the code creates. If a variable has an unknown value, then put a ? next to it.
        newnode = new PersonNode("Frank");
        x = newnode;
        newnode = new PersonNode("Jackie");
        newnode->next = x;
        x = newnode;
          

    2. Execute the following, additional code and draw the resulting diagram using the same instructions for drawing the diagram that were given in part a. Do not start from scratch. This code builds on the code that was executed in part a:
        newnode = new PersonNode("Sue");
        y = x->next;
        newnode->next = y;
        x->next = newnode;
          

  5. (10 points) Explain what is wrong with the following code segment. The intent of the code is to maintain a map keyed by name. For each name we keep track of that person's friends. The code is supposed to add friend2 to friend1's set of friends. You should assume that the input is error-free. Use no more than 3 sentences to describe the problem.
        map<string, set<string> > friendsMap;
        map<string, set<string> >::iterator friends_it;
        set<string> friendsSet;
        string friend1, friend2;
        while (cin >> friend1 >> friend2) {
           friends_it = friendsMap.find(friend1);
           friendsSet = friends_it->second;
           friendsSet.insert(friend2);
        }
    
    I inadvertently left out code that checked to see if friend1 was in the set. That meant that the code could have two issues, and I accepted either of the two issues as the correct answer. I meant for the correct answer to be the second issue. The bold-faced line in the above code has two issues. The first issue is that if friend1 is not in the set, then friends_it points to a sentinel node and when you try to access the second field, you will not retrieve a valid set. That means that the insert call in the following statement will seg fault. The second issue is that the bold-faced statement creates a copy of the set. The insert on the next line thus inserts into the copy, rather than the original set, and the original set does not get updated as it should.

  6. (10 points) You are given the following code fragment:
    	class Person {
    	  public:
    	    string name;
    	    int age;
    	};
    
    	Person *p;
    
    	p->name = "Nancy";
    	p->age = 25;
          
    Answer the following questions about the code fragment:

    1. Using 3 sentences or less, describe what is wrong with this code fragment. The problem is not with the class declaration--it is provided only for descriptive purposes.

      p is never initialized with a pointer to a Person object. As a result when the code attempts to assign "Nancy" and 25 to the name and age fields, it is trying to go through an invalid pointer. At some point this will most likely cause your program to seg fault.

    2. What C++ statement(s) need to be added to this code fragment to correct it and where should they be added. I want to see syntactically correct C++ code.

      After the declaration for p and before the two assignment statements, you need the following statement:

      		p = new Person();
      	      

  7. (12 points) Your boss has asked you to choose between two algorithms. The two algorithms have the following running times:
    	  Algorithm 1: T(n) = 60n3 + 5n*log n + 50000n + 20
    	  Algorithm 2: T(n) = 0.10 * 2n + n + 8
            

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

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

    3. The size of the input will be typically on the order of n > 50. Which of the two algorithms should you choose and why? You should choose algorithm 1 because algorithm 1 is a polynomial time algorithm whereas algorithm 2 is an exponential time algorithm. Polynomial time algorithms are always faster than exponential time algorithms for any n larger than a moderately sized number, such as 50.
  8. (10 points) You have been asked to print out all names in a multimap whose salaries lie between $15,000 and $20,000. The declaration for the multimap is:
    	multimap<int, string> salaries;
          
    The key is an integer denoting the person's salary and the value is a string representing the person's name. Below are two code fragments which will correctly print out the names and salaries. lower_bound(15000) returns an iterator that points to the first element in the multimap whose salary is greater than or equal to 15,000. lower_bound() has the same Big-O running time as the find function.

    (a)(b)
       multimap<int, string>::iterator mit;
    
       for (mit = m.begin(); mit != m.end(); mit++) {
          if (mit->first >= 15000 && mit->first <= 20000) {
             cout << mit->second << " " << mit->first << endl;
          }
       }
    						     
       multimap<int, string>::iterator mit;
    
       mit = m.lower_bound(15000);
       for (; mit != m.end(); mit++) {
          if (mit->first > 20000)
              return;
          else
              cout << mit->second << " " << mit->first << endl;
       }
    			  

    Make the following assumptions:

    Answer the following questions:

    1. Which code segment is more efficient (a or b)? 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. Code segment b takes advantage of the fact that the data structure is a map and that the salaries we need will be located contiguously in sorted order. It uses an O(log n) function, lower_bound(), to locate the starting salary and then looks only at the entries in the map that are in the stated range. Therefore its running time is O(log n). By contrast, code segment a does not take advantage of the fact that the data structure is a map and that the salaries are stored contiguously. It searches all entries in the map, which is an O(n) operation. Since O(log n) is better than O(n), code segment b is preferable to code segment a.

  9. (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<string> names;
    list<string>::iterator nit, nit1;
    list<string>::reverse_iterator rnit;
    names.push_back("joe");
    names.push_back("mary");
    names.push_front("nancy");
    names.push_front("tom");
    nit = names.begin();
    nit++;
    rnit = names.rbegin();
    nit1 = names.end();
    

Coding Solutions

  1. Write a function named avgWeight that takes a single argument, which is an STL list of pointers to User objects, and returns their average weight as a double. You are given the following class declaration for User:
    class User {
    public:
      string name;
      int weight;
    };
    
    double avgWeight(list<User *> &data) {
      int sum = 0;
      list<User *>::iterator lit;
      for (lit = data.begin(); lit != data.end(); lit++)
        sum += (*lit)->weight;
      return sum / (double)data.size();
     }
      

  2. Write a C++ program to score af contest. Standard input is composed of lines of text, where each line contains a person's first name and the name of a problem they have correctly solved. A person gets credit for solving the problem only if they are the first person to have solved the problem. The winner is the person or persons who correctly solves the most problems.

    Your job is to write a program that prints out the name of the winning person(s) and the number of problems they were credited with solving. Put a single space between the winner's name and the number of problems they solved. If there is a tie, then print the names of all of the winning persons in alphabetical order with each person printed on a new line.

    Here is a straightforward solution that uses a find to determine if a problem or contestant has been previously seen and if not, then an insert to insert the problem or contestant into the appropriate data structure:

    #include <map>
    #include <set>
    #include <iostream>
    using namespace std;
    
    int main() {
       set<string> problems;
       map<string, int> contestants;
       map<string, int>::iterator mit;
       int max;
       string name, problem;
    
       while (cin >> name >> problem) {
         // determine if a problem has been previously solved. If it has not
         // been previously solved then insert the problem into the problems
         // set and add 1 to the contestants score. If the contestant has not
         // been previously seen, then insert the contestant into the contestants
         // map and assign the contestant a score of 1.
          if (problems.find(problem) == problems.end()) {
             problems.insert(problem);
             mit = contestants.find(name);
             if (mit != contestants.end()) {
                mit->second++;
             }
             else 
                contestants[name] = 1;
          }
       }
       // find the winning score
       max = 0;
       for (mit = contestants.begin(); mit != contestants.end(); mit++) {
          if (mit->second > max) {
             max = mit->second;
          }
       }
       // print all winners in alphabetical order
       for (mit = contestants.begin(); mit != contestants.end(); mit++) {
          if (mit->second == max) {
             cout << mit->first << " " << mit->second << endl;
          }
       }
       return 0;
    }
    						     
    Here is a slightly more efficient version for scoring the contestant that combines the insert and find operations. It's a bit more complicated to read but performs only one O(log n) operation rather than two O(log n) operations. It takes advantage of the fact that insert for both sets and maps returns a pair where the first element is an iterator either to the inserted node or, if the key already existed, to the node for the pre-existing key, and the second element is a bool indicating whether or not the insert succeeded.
        while (cin >> name >> problem) {
          // If the insert succeeds, the second item in the return result will
          // be true. 
          problemsInsertResult = problems.insert(problem);
          if (problemsInsertResult.second) {
             // If the insert succeeds, then a new node with a contestant score
             // of 0 is added to the map. If the insert fails, then insert returns
             // an iterator to the existing node. In either insert returns
             // an iterator to the contestant's node and we can
             // then increment the contestant's score by 1
             contestantsInsertResult = contestants.insert(make_pair(name, 0));
             mit = contestantsInsertResult.first;
             mit->second++;
          }
       }