[Instructions] [Search] [Current] [Changes] [Syllabus] [Handouts] [Outlines] [Labs] [Assignments] [Examples] [Bailey Docs] [SamR Docs] [Tutorial] [API]
The exam was reformulated to be out of eighty (80) points, making one of the first three questions optional.
Each of the following problems has an assigned point value. While these values are partially influenced by the expected complexity or time requirements of each problem, there is no guarantee that higher points means harder or longer.
1. Improving the Merge in Merge Sort [20 points]
Herman and Helga Hacker recently learned about the legendary merge
sort algorithm and have decided that the traditional
merge subalgorithm is inefficient because it uses
additional space. They decide to replace the traditional merge with
the following algorithm (which appears as part of an
ExtendedVector class). Critique their solution.
/**
* Merge two contiguous ordered subvectors.
* pre: 0 <= start <= mid < end < size
* pre: the first subvector (start ... mid) is sorted. That is,
* for all i, start <= i < mid, elementAt(i) <= elementAt(i+1)
* pre: the second subvector (mid+1 ... end) is sorted. That is,
* for all i, mid+1 <= i < end, elementAt(i) <= elementAt(i+1)
* post: the whole subvector (start ... end) is sorted and contains
* the same elements as before (just in a different order).
* running time: O(end-start)
*/
public void merge(int start, int mid, ind end) {
int left=start; // The "cursor" for the left subvector
int right=mid+1; // The "cursor" for the right subvector
while ((left <= mid) && (right <= end)) {
// Make sure the smaller of the two current elements is
// now in the left subvector.
if (elementAt(right) < elementAt(left)) {
swap(left,right);
}
// Increment the counters
left = left + 1;
right = right + 1;
} // while
} // merge
There seems to be no clear rationale to their solution. While it makes sense to swap elements that are out of order, there is no guarantee that after the swapping the element swapped into the right subvector is in the right position. Consider the vector
3 4 1 5 L R
with subvectors [3 4] (sorted) and [1 5] (also sorted).
If start is 0, mid is 1, and end
is 3, then when we swap the 3 and the 1, we are
left with
1 4 3 5 L R
Now, the element at left is less than the element at
right, so we're done (more or less). However, the
resultant list is not sorted.
There are certainly other problems with the algorithm. For example, even if the strategy were generally correct, the Hackers make no attempt to deal with different size sublists (which will happen occasionally).
Can we improve their algorithm? Possibly. We might try moving the the
left cursor and not the right cursor. This
will have a problem with running time because we'll, in effect, insert
the first element in the right list into the left list and then still be
left with two lists that need to be merged. We could insert the
elements from the second list one-by-one into the first list, but that's
O(n^2) time. As far as I know, no one has figured out how to do
in-place merging in O(n) time.
Note that some of you weren't satisfied with giving a non-working example and felt you had to say more. This was fine, until you went so far as to say something incorrect, in which case I felt it necessary to take off some points.
2. Biased Notation [20 points]
You may recall that the IEEE standard representation for single precision floating point numbers uses a biased notation for the exponent. In a biased notation, to represent the signed value N, you instead represent the unsigned value bias+N. For the following problems, you should use eight bits and a bias of 128.
What is the appropriate bias 128 representation of -17?
(-17) + 128 = 111 = 64 + 32 + 8 + 4 + 2 + 1
01101111
What is the appropriate bias 128 representation of 123?
123 + 128 = 251 = 128 + 64 + 32 + 16 + 8 + 2 + 1
11111011
What is the two's complement representation of -17?
Recall that in two's complement notation to negate a number you flip the bits and add 1.
17 = 16 + 1, which is represented as 00001001. Flip the bits, giving 11101110. Add 1, giving 11101111.
Hmmm ... that's the same as the bias-128 representation except that the leftmost bit is flipped.
What is the two's complement representation of 123?
123 = 64 + 32 + 16 + 8 + 2 + 1, which is represented as 01111011.
Hmmm ... once again it's the bias-128 representation with the leftmost bit flipped. Could I prove that?
What does 11010001 represent in bias 128 notation?
What does 01101101 represent in bias 128 notation?
What do you get if you add -5 and 7 (in bias 128 notation) using the standard addition strategy for unsigned binary numbers?
Represent -5: (-5) + 128 = 123 = 64 + 32 + 16 + 8 + 2 + 1, so 01111011.
Represent 5: 5 + 128 = 133 = 128 +4 + 1, so 10000101.
Represent 7: 7 + 128 = 135 = 128 + 4 + 2 + 1, so 10000111.
Represent -7: (-7) + 128 = 121 = 64 + 32 + 16 + 8 + 1, so 01111001.
1111111
-5: 01111011
7: +10000111
---------
100000010
We drop the leftmost 1 (since we're limited to eight bits), giving 00000010.
Hmmm ... that's 2 in standard binary notation. However, we were working in excess-128, so we need to subtract 128, giving -126. Not very encouraging. However, we are off by only 128.
What do you get if you add 5 and 7?
111
5: 10000101
7: +10000111
---------
100001100
We drop the leftmost bit, giving 00001100. Hmmm ... that's 12, but we need to subtract 128, giving -116. Not good. Perhaps we need to add 128 to the result?
What do you get if you add 5 and -7?
1
5: 10000101
-7: +01111001
---------
11111110
Yay! No overflow bit. However, the result is 254. When we subtract 128 we still get 126.
What do you get if you add -5 and -7?
1111 11
-5: 01111011
-7: +01111001
---------
11110100
Okay, that's 244. Subtract 128 we get 116.
What does this suggest?
That simply adding isn't enough. But wait! Every answer was off by 128 (-126+128=2; -116+128=12, 126-128=-2; 116-128=-12). Why is that? Basically, we were adding (X+128) + (Y+128) which gives (X+Y+128)+128, so we wouldn't expect to get the right number. Instead, we need to subtract 128 to get the right answer. However, because of the way the numbers are designed, adding 128 and subtracting 128 end up doing the same thing (flipping the left bit).
So, to add two numbers represented in excess-128, add them as you would any unsigned integers and then flip the leftmost bit.
3. Tree Traversal [20 points]
Our friends the Hackers are back again. This time, they've written a "generic" tree traversal algorithm. They claim "it can do both breadth-first and depth-first traversal of binary trees; all you have to do is select the right argument". With some prodding, they admit that their method only does preorder left-to-right traversals, but stick to their claim about "both breadth-first and depth-first". For once they may be right. Explain their claim.
/**
* Print all the elements of a tree, using a linear structure
* to help.
*/
public void printNodes (
SimpleOutput out,
BinaryNode root,
Linear collection)
{
// Just in case the collection has elements, reset it
collection.reset();
// Add the root.
collection.add(root);
// As long as the collection is not empty, remove an
// element, print it out, then add its children.
while (!collection.empty()) {
Node next = collection.remove();
out.println(next.toString() + " ");
if (next.getLeft() != null) {
collection.add(next.getLeft());
}
if (next.getRight() != null) {
collection.add(next.getRight());
}
} // while the collection is not empty.
} // printNodes
This should seem very similar to our puzzle solving algorithm. If we use a stack as the linear structure, then we do a depth first search. If we use a queue as the linear structure, then we do a breadth-first search. Why? Using a stack, we complete the subtree for one child before we get to the next child (since we'll be pushing all the children of the first child before its sibling). Using a queue, we put the children of a node after its sibling.
Consider the following tree
A
B C
D E F G
If we pass in a queue, we get:
Observe that this is breadth-first, left-to-right, preorder.
If we pass in a stack, we get
Hmmm ... not quite left-to-right, but certainly depth-first and preorder. So, the hackers aren't quite as talented as I suggested.
A number of you made assumptions about the addition and removal policy for the Linear structure and forgot that both stacks and queues are linear structures. This made it difficult to get this question right.
4. List Deletion [40 points]
Believe it or not, our friends the Hackers have written a relatively
elegant implementation of doubly linked lists. Unfortunately, they've
forgotten to include a remove method and have asked you
to write one. Your goal is to write and document a routine that removes
all copies of an element from a doubly-linked list.
They've told you
that they've used
rebelsky.util.BinaryNode
for their nodes and that the attributes of their class are as follows:
public class TheGreatestDoublyLinkedListEver {
/**
* The first element in the list.
*/
protected BinaryNode first;
/**
* The last element in the list.
*/
protected BinaryNode last;
/**
* The current element in the list.
*/
protected BinaryNode current;
/**
* The size of the list.
*/
protected int size;
...
} // TheGreatestDoublyLinkedListEver
Write and document the remove method that removes all
copies of an element from the list. Make sure your documentation is
at least as thorough as my typical documentation. Also make sure to
indicate the running time of your algorithm.
/**
* Remove all copies of an element, elt, from the current list.
* Precondition: (none)
* Postcondition: the list no longer contains any values
* equal to of elt.
* Postcondition: if the cursor was equal to elt, then
* the cursor refers to the start of the list (unless the
* list is now empty).
* Postcondition: any elements not equal to elt are still in
* the list and still in the same order.
*
* @return The number of elements deleted.
*/
public int remove(Object elt) {
// The number of elements we've removed
int removed = 0;
// The element we're currently looking at
BinaryNode counter = front;
// The next and previous elements (not strictly necessary,
// but they do make it a little easier to read).
BinaryNode prev, next;
// Step through each element until we reach the end. If we
// identify an equal element, then we delete the element
// by changing the previous reference of the next node and the
// next reference of the previous node.
while (counter != null) {
// Should we delete the element?
if (counter.getContents().equal(elt)) {
next = counter.getNext();
prev = counter.getPrev();
// Is there a previous element? If so, update it.
if (prev != null) { prev.setNext(next); }
// Is there a subsequent element? If so, update it.
if (next != null) { next.setPrev(prev); }
// Have we affected the front, back, or current
// element of the list? Note that we use "==" because
// we actually want "points to the same node".
if (counter == front) { front = next; }
if (counter == back) { back = prev; }
if (counter == current) { current = front; }
// Update the size and number of elements we've removed
removed = removed + 1;
size = size - 1;
} // if we should delete the element
// Move on to the next element
counter = counter.getNext();
} // while
// Done. Return the number of elements we've removed.
return removed;
} // Remove(Object)
Does this work with an empty list? Certainly. The body of the while loop is never executed. Should we restrict the method to nonempty lists? Probably not, but I didn't penalize those of you who did (unless you then failed to handle empty lists appropriately).
Does this work if it clears out the list? Well, it suffices to check
the single element list (since longer lists will be reduced to the
single elelment list). At that point, current,
counter, front, and back will
all point to the element that's equal and prev and
next. will be null. So, when we update all of them,
everything becomes null, which is how we represent the empty list.
What is the running time? It's an O(n) algorithm because we need to step through each element of the list. Can we do better? Probably not.
What were some common mistakes? [Listed so that those of you who didn't make them don't make them next time, and so those of you who made some see that others made similar and different errors.]
prev.setNext(...)
(requires that prev be nonnull). [Similar for
setting the previous reference of the next node.]
Each of the following questions is worth two points if answered well (and zero if answered poorly).
How does Bob Cadmus's lecture on electronic imaging technology relate to what we've learned in CS152?
Good answers included:
List three things that Vannevar Bush should be famous for.
[Instructions] [Search] [Current] [Changes] [Syllabus] [Handouts] [Outlines] [Labs] [Assignments] [Examples] [Bailey Docs] [SamR Docs] [Tutorial] [API]
Disclaimer Often, these pages were created "on the fly" with little, if any, proofreading. Any or all of the information on the pages may be incorrect. Please contact me if you notice errors.
Source text last modified Tue Dec 29 09:13:41 1998.
This page generated on Tue Jan 12 11:47:39 1999 by SiteWeaver.
Contact our webmaster at rebelsky@math.grin.edu