CS140 Final Exam - May 6, 2011 - Answers & Grading


Question 1

This is straightforward. First, you need to double-check that the node is not the sentinel. Next, you unhook it from the list by setting its previous node's blink pointer to its blink pointer, and then the subsequent node's flink. pointer to its flink. Finally, you delete the node and update the list's size.

void Dlist::Erase(Dnode *n)
{
  if (n == sentinel) {
    cerr << "You shouldn't delete the sentinel\n";
    exit(1);
  }
  
  n->flink->blink = n->blink;
  n->blink->flink = n->flink;
  delete n;
  size--;
}

Grading:


Question 2

Grading: 0.5 points for the index and 0.5 for the probes. For the probe values, I used whichever gave you more points, as long as it was consistent.


Question 3

Part A: This program prints out the last half of the lines of standard input. It pushes every line at the end of lh, and then for every other line, it deletes the first element of lh. Thus, the output is:

D
E
F
G

Part B: The difference between deques and vectors is that deques allow for efficient insertion and deletion from both ends of the data structure. Vectors only allow for efficient pushing and popping from the rear of the data structure. Therefore, if the vector is size n, then the erase() operation will be O(1) for deques but O(n) for vectors. So the vector implementation is far worse. The difference in performance will be most marked when standard input has a lot of lines. For example, with 100000 lines, the deque version will run with roughly 100,000 operations. The vector version will take roughly 10,000,000,000 operations.

To exemplify, in this directory, the vector version is in tailhalfv.cpp and the deque version is in tailhalfd.cpp. The file input.txt is 100,000 lines:

UNIX> time tailhalfd < input.txt > /dev/null
0.183u 0.034s 0:00.22 95.4%	0+0k 0+0io 0pf+0w
UNIX> time tailhalfv < input.txt > /dev/null
30.543u 0.111s 0:31.08 98.6%	0+0k 0+0io 0pf+0w
UNIX> 
Grading: 2 points for the output, 2 points for the explanation, 2 points for the input file/explanation.


Question 4

From the definition of big-O, we need to prove that there exist positive constants c and x0 such that for all values of x > x0:

7x + 45 < cx

We can choose easy values: c = 100 and x0 = 1. At that point, 7x+45 is equal to 52, and 100x is equal to 100. As x increases, 100x grows faster than 7x+45. Thus, for all values of x greater than one, 7x + 45 < 100x, and therefore 7x+45 = O(x).

Grading: 3 points for the big-O definition, 2 points for choosing c and x0, 1 point for tying it all together.


Question 5

Part A:We simply build by finding where each number would go, and then putting it there. Here is the construction. The final answer is the tree in the blue box:

Part B:

Part C: Since the node has two children, you find the greatest node in the left subtree "gonna," delete it and then replace "it's" with "gonna".

You could have replaced "it's" with "night" too.

Grading: 2 points for parts A and C, 2 points for each traversal.


Question 6

First, let's annotate each node with its height:

Now, this is an AVL tree if the difference between the height of each node's children is at most one. This is true for every node on the tree (if a node's child is empty, its height is -1), so the tree is indeed an AVL tree.

When we insert "what", we traverse each node on the path from "what" to the root, readjust the height if necessary and check the balance condition. When we do this, we see that "is" is not balanced:

This is a zig-zig imbalance, so we rotate once about "that" to yield the following tree:

Grading: 2 points for the definition, 2 points for labeling the tree so that it matches the definition, 2 points for the insertion.


Question 7

This problem is perfect for a map. The map should be keyed on names and have the number of votes as the val. We create it by traversing the votes array, and if the person hasn't voted for himself/herself, incrementing the number of votes in the map.

Next, traverse the map to find the maximum number of votes in the map.

Finally, traverse the map again and find out who has that number of votes. If more than one person has it, return the empty string immediately. Otherwise, return the person. The code is in topcode.cpp:

string RabbitVoting::getWinner(vector <string> names, vector <string> votes)
{
  map <string, int> v;
  map <string, int>::iterator vit;
  int i;
  string winner;

  // Determine everyone's number of votes:

  for(i = 0; i < votes.size(); i++) {
    if (votes[i] != names[i]) v[votes[i]]++;
  }

  // Find the maximum number of votes:

  i = 0;
  for (vit = v.begin(); vit != v.end(); vit++) {
    if (vit->second > i) i = vit->second;
  }

  // Find the winner, returning "" if there is more than one:

  winner = "";
  for (vit = v.begin(); vit != v.end(); vit++) {
    if (vit->second == i) {
      if (winner != "") return "";
      winner = vit->first;
    }
  }
  return winner;
}

You could have tried running through the names vector, counting the legal votes for each names[i] by running through the votes vector, and then returning the person with the maximum. However, that would be O(n2), and would run too slowly given that names can have 100000 elements.

Grading: 4 points for calculating votes, 4 points for calculating the max and returning the correct name. If you did the O(n2) algorithm, you lost a point and I didn't explain it, so if you're confused as to why you lost a point on the first part, that's probably the explanation.