## CS302 Midterm, Fall, 2016

### Question 1: 20 points

Part A: The links define the following instance of disjoint sets:

{ 0, 1, 3, 6, 7, 8, 11, 12, 13 }, { 2, 4, 9, 14 } {5, 13} { 10 }
Part B: Union by size. This is because the ranks[] entry of the heads of the set are equal to the sets' sizes. They clearly don't equal the heights (e.g. the height of the set rooted by 1 is not 9), and even if you used path compression, there's no way to get a rank of 9 with this few elements.

Part C: See the picture:

• d.Find(0) = 1.
• d.Find(15) = 1.
• d.Find(14) = 2.
• d.Find(10) = 10.
• d.Union(1,2) = 1. This is because the rank of set 1 is bigger than the rank of set 2, so node 1 becomes the parent and node 2 becomes the child.

Part D: The first thing we do is view the vector as a heap:

Now, you call percolate_down() on the nodes with 40, 46 and 71:

Next, you call percolate_down() on the nodes with 56 and 86:

And finally, you call percolate_down() on the root:

Print that out as a vector:

{ 5, 10, 68, 32, 16, 71, 75, 40, 56, 46, 20, 86 }

Doing this by calling Push() on each element of the vector is not only the incorrect way to do it, it's a huge pain, as you have to call percolate_up() 11 times. If you did that, the proper answer was:

{ 5, 16, 10, 40, 20, 68, 71, 75, 46, 56, 32, 86 }

• Part A: 4 points.
• Part B: 2.5 points.
• Part C: 7.5 points (1.5 points per answer)
• Part D: 6 points. You got 5 points if you did the Push() solution correctly, and you got partial credit for valid heaps that didn't fit either paradigm (and that had all the elements).

### Question 2: 20 points

I reiterated during the test: I wanted the running time of the procedure. That is specified clearly on the exam. That will be defined by the running time of the most costly operation, but that doesn't mean it is equal to the running time of the most costly operation. For example, in Proc_F(), the most costly operation is the Pop() call, which is O(log n). However, you're calling Pop() n times, so the running time of the procedure is O(n log n).

Grading here was 2 points per part. I gave some partial credit.

Also, I took off if your answers demonstrated that you didn't really "get" big-O -- answers like O(n+3), or O(n/2), or O(n) + O(n2).

• Proc_A(): This calls C.insert() (n-1) times. Since list insertion is O(1), this is O(n). 0.5 points for O(1).

• Proc_B(): This is your classic summation of i going from zero to (n-1):

This is equal to (n(n-1)/2), which is O(n2).

• Proc_C(): This is a classic power set enumeration, straight from the lecture notes: O(2n).

• Proc_D(): This procedure traverses a set, and calls push_back() on each element. That is O(n).

• Proc_E(): Creating a heap from a vector is O(n).

• Proc_F(): The Pop()'s are each O(log n), so this one is O(n log n).

• Proc_G(): This is the same as the last problem -- The insert()'s are each O(log n), so this one is O(n log n).

• Proc_H(): Once again, this is the same as the last problem -- The insert()'s are each O(log n), so this one is O(n log n).

• Proc_I(): Union operations are O(1), so this is O(n).

• Proc_J(): The best implementation of disjoint sets is Union by Rank with Path Compression. Each Find() is O(α(n)), so this is O(n α(n)).

• 1 point per part.
• You get 0.5 points for answering k on part F.
• You get 0.5 points for answering j on part H.
• You get 0.2 points for answering r on part J.
• You get 0.3 points for answering l on part L.
• You get 0.3 points for answering j on part N.

### Question 3: 20 points

Part A: This one follows the enumeration lecture notes. Let l be equal to s.size(). You will enumerate all numbers from 0 to ln-1, and treat each number like a n-digit number in base l. You'll extract those digits with div and mod, and use them as indices into s. The program q3.cpp implements proc() and also implements a main() that allows you to specify n and s on the command line.

 ```void proc(int n, string s) { int top; int i, j, index; /* Calculate the total number of strings. */ top = 1; for (i = 0; i < n; i++) top *= s.size(); /* Now, enumerate a separate index for each string. You will calculate the individual characters using div/mod, as explained in the enumeration lecture notes. */ for (index = 0; index < top; index++) { i = index; for (j = 0; j < n; j++) { printf("%c", s[i%s.size()]); i /= s.size(); } printf("\n"); } } ```

 ```UNIX> g++ -o q3 q3.cpp UNIX> q3 2 a aa UNIX> q3 2 ab aa ba ab bb UNIX> ``` ```UNIX> q3 2 abc aa ba ca ab bb cb ac bc cc UNIX> ``` ```UNIX> q3 3 ab aaa baa aba bba aab bab abb bbb UNIX> ```

Part B: ln, where l equals s.size().

Part A was worth 16 points, and Part B was worth 4. If you gave the right answer for Part B, but it didn't match your implementation, then you got 3 points. I gave partial credit to power set enumerations, which typically got confused, because you didn't know what to do with n.

### Question 4: 16 points

The answers for both questions 4 and 5 are in q45.cpp. Question 4 is straightforward bit arithmetic:

 ```vector do_transpose(vector m) { vector rv; int i, j; rv.resize(m.size(), 0); for (i = 0; i < m.size(); i++) { for (j = 0; j < m.size(); j++) { if (m[i] & (1 << j)) { rv[j] |= (1 << i); } } } return rv; } ```

• Resizing the return vector: 2 points.
• Having a double-nested for loop on m.size(): 2 points.
• Extracting the bits correctly: 4 points
• Setting the bits correctly: 4 points
• Getting i and j correct with the (have to be using bit arithmetic to get this): 4 points
As always, I gave partial credit.

### Question 5: 20 points

Question 5 is a nuts and bolts program that uses sscanf() or stringstreams to read the command line and check the formatting. There are three errors that I can think of, and you should test for each:
• The argument is not in the format "0x"-number-in-hex. You can test the return value of sscanf() for that error.

• There are more than 32 arguments -- in that case, m is too big.

• The last one is subtle, but I included it in my example -- if a number has any bits set that are greater than the first m.size() bits, then that too is an error. For example, if there are four elements in the matrix, and one of them is, say, 0x10, then it has too many bits set (because 0x1f is equal to 10000 in binary). To test for this, you can do a simple for loop that starts at (1 << m), and stops at 0x80000000, using bit arithmetic to test to see if the bits are set. I took an easier path below, by clearing those high order bits, and then testing to see if the result equals the original.

 ```int main(int argc, char **argv) { int i, v, mask; vector m; vector t; /* Reading the command line. This loop was worth 12 points. */ for (i = 1; i < argc; i++) { if (sscanf(argv[i], "0x%x", &v) != 1) { printf("Bad command line argument %s\n", argv[i]); exit(1); } m.push_back(v); } /* Too many words on the command line. Two points. */ if (m.size() > 32) { printf("Too many items in m: must be <= 32\n"); exit(0); } /* Checking for stray bits. Two points. */ if (m.size() < 32) { mask = (1 << m.size()) - 1; for (i = 0; i < m.size(); i++) { if ((m[i] & mask) != m[i]) { printf("Bad item -- 0x%x -- too many bits set.\n", m[i]); } } } /* Call do_transpose and print. Four points. */ t = do_transpose(m); for (i = 0; i < t.size(); i++) printf("0x%x\n", t[i]); return 0; } ```

• Looping from 1 to argc: 2 points
• Looping from 0 to argc: 1 point
• Reading from standard input rather than the command line: 0.5 points
• Reading the "0x": 1 point
• Reading the hex correctly using sscanf() or a stringstream: 5 points.
• Printing an error if there is no "0x": 2 points
• Printing an error if the number is not proper hex: 2 points
• Printing an error when there are more than 32 words: 2 points
• Printing an error when stray bits are set: 2 points
• Calling do_transpose(): 1 point
• Outputting a line per element of the vector: 1 point
• Outputting proper hex: 1 point
• Outputting the "OX": 1 point