Class Name: Algorithms package: showThen insert the following content ( click to show ) This class illustrates the algorithm behavior visually in standard output. It is intended for usage only in small demonstration arrays.
Class Name: Algorithms package: countThen insert the following content ( click to show ) This class can be used to count the number of comparisons and data movements in sorting. The overhead is minimal, and so it can be used as the actual sorting algorithms.
Class Name: Counter package: countThen insert the following content ( click to show ) This is an auxiliary class used by count.Algorithms to provide external counters representing the comparisons and data movements which we want to count.
for (int i = 0; i < n - 1; ++i) {
// set selected_index so that a[selected_index] smallest in a[i]..a[n-1]
int selected_index = i;
for (int j = i + 1; j < n; ++j) {
if (a[j] < a[selected_index]) {
selected_index = j;
}
}
if (selected_index != i) { // swap a[i] & a[selected_index]
int tmp = a[selected_index];
a[selected_index] = a[i];
a[i] = tmp;
}
}
1. public static <E> void somethingSort(
E[] a, int fromIndex, int toIndex, Comparator<? super E> c);
2. public static <E> void somethingSort(E[] a, Comparator<? super E> c);
3. public static void somethingSort(Object[] a, int fromIndex, int toIndex);
4. public static void somethingSort(Object[] a);
Only the first of these contains actual sort code; the other 3 are derived:
2. public static <E> void somethingSort(E[] a, Comparator<? super E> c) {
somethingSort(a, 0, a.length, c);
}
3. public static void somethingSort(Object[] a, int fromIndex, int toIndex) {
Comparator<Object> c = new Comparator<Object>() {
@Override
public int compare(Object lhs, Object rhs) {
return ((Comparable)lhs).compareTo(rhs);
}
};
somethingSort(a, fromIndex, toIndex, c);
}
4. public static void somethingSort(Object[] a) {
somethingSort(a, 0, a.length);
}
In the case of selection sort, the primary sorting algorithm is this:
public static <E> void selectionSort(
E[] a, int fromIndex, int toIndex, Comparator<? super E> c) {
for (int i = fromIndex; i < toIndex - 1; ++i) {
int selected_index = i;
for (int j = i + 1; j < toIndex; ++j) {
if (c.compare(a[j], a[selected_index]) < 0) {
selected_index = j;
}
}
if (selected_index != i) {
swap(a, selected_index, i);
}
}
}
1. public static <E> void somethingSort(List<E> list, Comparator<? super E> c); 2. public static <E extends Comparable<? super E>> void somethingSort(List<E> list);The second is derived from the first:
2. public static <E extends Comparable<? super E>> void somethingSort(List<E> list) {
Comparator<E> c = new Comparator() {
@Override
public int compare(E lhs, E rhs) {
return lhs.compareTo(rhs);
}
};
somethingSort(list, c);
}
There are two conceivable ways to write the primary list sorting alorithm:
public static <E> void selectionSort(List<E> list, Comparator<? super E> c) {
E[] a = (E[]) list.toArray();
selectionSort(a, c);
for (int i=0; i < a.length; ++i)
list.set(i, a[i]);
}
public static <E> void selectionSort(List<E> list, Comparator<? super E> c) {
for (int i = 0; i < list.size() - 1; ++i) {
int selected_index = i;
for (int j = i + 1; j < toIndex; ++j) {
if (c.compare(list.get(j), list.get(selected_index)) < 0) {
selected_index = j;
}
}
if (selected_index != i) {
// this convenience operator is basically 3 set operations:
Collections.swap(list, selected_index, i);
}
}
}
In the case of the list being a LinkedList, the latter approach is probably out of the question since get and set operations may not be O(1) time.
If the list were an ArrayList, the most efficient thing would be to sort the internal array directly, but there is apparently no way to do so, since the toArray() member returns a copy of the internal array.
for (int i = 0; i < a.length; ++i) list.set(i, a[i]);which suits a RandomAccess object like an ArrayList. In contrast, for a LinkedList object this code would be better:
list.clear(); for (int i = 0; i < a.length; ++i) list.add(a[i]);This latter code, however, does not suit an Arrays.ArrayList object (as is returned by Arrays.asList(A)) which does not support removal needed the clear() function. A compromise solution might be to write the code like this:
public static <E> void selectionSort(List<E> list, Comparator<? super E> c) {
E[] a = (E[]) list.toArray();
selectionSort(a, c);
if (list instanceof RandomAccess) {
for (int i = 0; i < a.length; ++i) {
list.set(i, a[i]);
}
} else {
list.clear();
for (int i = 0; i < a.length; ++i)
list.add(a[i]);
}
}
}
We could effectively write the code by having an element move down to its correct position by swapping with the one below and then moving down, etc. However, swapping is more costly than simply shifting an element (by a factor of 3) and so it is more efficient to think of shifting the element up.
Here is the code we'd use for an array: public static <E> void insertionSort(
E[] a, int fromIndex, int toIndex, Comparator<? super E> c) {
for (int i = fromIndex + 1; i < toIndex; ++i) {
E insert_value = a[i]; // pull the i-th value "out"
int j = i;
for (; j > fromIndex; --j) { // j = "a first correct position"
if (c.compare(a[j - 1], insert_value) > 0) {
a[j] = a[j - 1]; // shift the value up
} else {
break;
}
}
if (j != i) { // if j is not the same as i
a[j] = insert_value; // insert into the j-th position
}
}
}
}
(n-1) + (n-2) + ... + 1 = n*(n-1)/2The number of data movements is at best 0 if the array is already sorted. The worst case is a reverse-sorted array in which case we must do a swap on each iteration. Counting a swap as 3 data movements we get a worst case of 3*(n-1). For insertion sort, the number of comparisons and data movement may both vary. In the best case of a sorted array, there are n-1 data movements (to save the insert element) and n-1 comparisons. In the worst case of a reverse-sorted array there n*(n-1)/2 comparisons and the same number of shifts, each counting for 1 data movement. A final table illustrates the analysis of the two algorithms:
|
best case (sorted) |
worst case (reverse sorted) |
|||
|---|---|---|---|---|
| comparisons | data movements | comparisons | data movements | |
| selection sort | n*(n-1)/2 | 0 | n*(n-1)/2 | 3*(n-1) |
| insertion sort | n-1 | n-1 | n*(n-1)/2 | (n-1) + n*(n-1)/2 |
if (c.compare(a[j], a[selected_index]) < 0) {
selected_index = j;
}
if (c.compare(a[j 1], insert_value) > 0) {
a[j] = a[j 1];
}
The other positive effect of stability is that the necessary usage of comparions makes the code slightly more efficent in selecttionSort and considerably more efficient in insertionSort. Changing ">" to ">=" in insertionSort would create more comparisons and data moves when the array has a significant number of "equals."
Counter counter = new Counter();and then made available for usage by the algorithm by the function:
count.setCounter( counter );A single comparison is registered by the execution:
incComparisons(1);A single data movement is registered by the execution:
incDataMoves(1);Here are the details of the usage with counting code inserted into selectionSort and insertionSort: The actual incrementing only takes place if counter is non-null, giving us the flexibility of easily turning counting off and on as desired. Additionally, we can move counter objects in and out to suit our needs.