Overview
What happens when there’s a fractional portion of the average?
It should round toward zero.
Why doesn’t my algorithm work?
Run it through by hand (with pictures).
Run it in the debugger.
Compare the outputs.
You have an array with three values, which we’ll call red, white, and blue. They are in no particular order. Your goal is to rearrange it so that all of the red are at the left, all the blue are at the right, and all the white are in between.
Sample input:
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| B | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | R |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Our invariant
+----------------+--------------+-------------+--------------+
| red | white | unknown | blue |
+----------------+--------------+-------------+--------------+
| | | | |
0 r i b length
Sam tends to put numbers at the left of the cell the number.
How do you make progress, while maintaining invariant?
swap(A, i++, r++);
swap(A, i, --b);
When do you stop? When i = b (or i >= b, if we’re lazy like one of the other Sam’s)
+----------------+----------------------------+--------------+
| red | white | blue |
+----------------+----------------------------+--------------+
| | | |
0 r i,b length
Initialization: What values of i, r, and b will allow us to ensure that the invariant is met before we enter the loop?
+----------------+----------------------------+--------------+
| unknown |
+----------------+----------------------------+--------------+
| |
0,r,i b,length
Running the algorithm
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| B | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | R |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| |
0,r,i b,length
A[i] = A[0] is B, swap(A,i,--b)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | |
0,r,i b length
A[i] = A[0] is R swap(A,i++,r++)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | |
0 r,i b length
A[i] is R swap(A, i++, r++)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | |
0 r,i b length
A[i] is W, increment i
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | |
0 r i b length
A[i] is W, increment i
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | W | W | R | B | W | R | W | W | W | R | W | B | R | W | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | |
0 r i b length
A[i] is R, swap(A, i++, r++)
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| R | R | R | W | W | B | W | R | W | W | W | R | W | B | R | W | B |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | |
0 r i b length
Invariants can help, particularly with nationalistic array problems!
Suppose we want a functional interface for a sorter for general arrays.
What does it look like?
public interface SorterO<T> {
/**
* Sort arr, returning a new array.
*/
public T[] sort(T[] arr, Comparator<? super T> compare);
} // interface SorterO<T>
public interface SorterI<T> {
/**
* Sort arr in place.
*/
public void sort(T[] arr, Comparator<? super T> compare);
} // interface SorterI<T>
<? super T>
means “T or any superclass or interface of T”. If I have
an Object comparator, I can use that to sort strings. If I have a person
comparator, I can use that to sort students. The ugly question mark
notation lets us write slightly more general code.
This comparator cannot use the parts of T that are custom to T.
In place or out of place?
Suppose SSI implements SorterI and we need a SorterO, what do we do?
public SSO<T> implements SorterO<T> {
public T[] sort(T[] values, Comparator<? super T> comparator) {
SorterI ssi = new SSI();
T[] copy = values.clone();
ssi.sort(copy, comparator);
return copy;
} // sort
} // SSO<T>
Remember: Preconditions and postconditions are adversarial. If you don’t put sufficiently many preconditions on, your client will call your method in such a way that it breaks, and then sue you. If you don’t put in sufficiently many postconditions, the programmer will take the lazy way out.
What do you make the preconditions your sort
method?
What do you make the postconditions your sort
method?
Sorting algorithms rearrange elements so that “smaller” move to the left and “larger” elements move to the right.
What about equal elements? A stable sorting algorithm maintains the order of equal elements.
A benefit to stable sorting algorithms; You can sort by one comparator, then another, and preserve some aspects of the first comparator.
What are some particular kinds of cases you’d pay attention to?
How would you test systematically?
How would you test “randomly”? (Don’t assume you have access to another
sort
method.)
Idea: Divide the array into sorted and unsorted. Repeatedly insert the first element from the unsorted part into the sorted part.
Invariant?
Stable?
Idea: Divide the array into sorted and unsorted. Repeatedly insert the first element from the unsorted part into the sorted part.
Invariant?
Stable?