Replace Arrays.sort() with an efficient sort algorithm

This change reuses the existing insertion sort (which was previously what
Arrays.sort() executed) in a full intro sort pipeline.

The implementation is based on the Musser paper on intro sort (Musser,
David R. "Introspective sorting and selection algorithms." Softw., Pract.
Exper. 27.8 (1997): 983-993.) and Wikipedia's current description of the
heap sort: http://en.wikipedia.org/wiki/Heapsort.

Signed-off-by: Johannes Schindelin <johannes.schindelin@gmx.de>
This commit is contained in:
Johannes Schindelin 2013-11-04 00:27:04 -06:00
parent 8b60a32f11
commit 605701e40a
2 changed files with 130 additions and 1 deletions

View File

@ -148,8 +148,100 @@ public class Arrays {
});
}
private final static int SORT_SIZE_THRESHOLD = 16;
public static <T> void sort(T[] array, Comparator<? super T> comparator) {
// insertion sort
introSort(array, comparator, 0, array.length, array.length);
insertionSort(array, comparator);
}
private static <T > void introSort(T[] array,
Comparator<? super T> comparator, int begin, int end, int limit)
{
while (end - begin > SORT_SIZE_THRESHOLD) {
if (limit == 0) {
heapSort(array, comparator, begin, end);
return;
}
limit >>= 1;
// median of three
T a = array[begin];
T b = array[begin + (end - begin) / 2 + 1];
T c = array[end - 1];
T median;
if (comparator.compare(a, b) < 0) {
median = comparator.compare(b, c) < 0 ?
b : (comparator.compare(a, c) < 0 ? c : a);
} else {
median = comparator.compare(b, c) > 0 ?
b : (comparator.compare(a, c) > 0 ? c : a);
}
// partition
int pivot, i = begin, j = end;
for (;;) {
while (comparator.compare(array[i], median) < 0) {
++i;
}
--j;
while (comparator.compare(median, array[j]) < 0) {
--j;
}
if (i >= j) {
pivot = i;
break;
}
T swap = array[i];
array[i] = array[j];
array[j] = swap;
++i;
}
introSort(array, comparator, pivot, end, limit);
end = pivot;
}
}
private static <T> void heapSort(T[] array, Comparator<? super T> comparator,
int begin, int end)
{
int count = end - begin;
for (int i = count / 2 - 1; i >= 0; --i) {
siftDown(array, comparator, i, count, begin);
}
for (int i = count - 1; i > 0; --i) {
// swap begin and begin + i
T swap = array[begin + i];
array[begin + i] = array[begin];
array[begin] = swap;
siftDown(array, comparator, 0, i, begin);
}
}
private static <T> void siftDown(T[] array, Comparator<? super T> comparator,
int i, int count, int offset)
{
T value = array[offset + i];
while (i < count / 2) {
int child = 2 * i + 1;
if (child + 1 < count &&
comparator.compare(array[child], array[child + 1]) < 0) {
++child;
}
if (comparator.compare(value, array[child]) >= 0) {
break;
}
array[offset + i] = array[offset + child];
i = child;
}
array[offset + i] = value;
}
private static <T> void insertionSort(T[] array,
Comparator<? super T> comparator)
{
for (int j = 1; j < array.length; ++j) {
T t = array[j];
int i = j - 1;

View File

@ -3,6 +3,41 @@ public class Arrays {
if (! v) throw new RuntimeException();
}
private static <T extends Comparable<T>> void expectSorted(T[] array) {
for (int i = 1; i < array.length; ++i) {
expect(array[i - 1].compareTo(array[i]) <= 0);
}
}
private static int pseudoRandom(int seed) {
return 3170425 * seed + 132102;
}
private static <T extends Comparable<T>> int shuffle(T[] array, int seed) {
for (int i = array.length; i > 1; --i) {
int i2 = (seed < 0 ? -seed : seed) % i;
T value = array[i - 1];
array[i - 1] = array[i2];
array[i2] = value;
seed = pseudoRandom(seed);
}
return seed;
}
public static void testSort() {
Integer[] array = new Integer[64];
for (int i = 0; i < array.length; ++i) {
array[i] = Integer.valueOf(i + 1);
}
;
int random = 12345;
for (int i = 0; i < 32; ++i) {
random = shuffle(array, random);
java.util.Arrays.sort(array);
expectSorted(array);
}
}
public static void main(String[] args) {
{ int[] array = new int[0];
Exception exception = null;
@ -94,5 +129,7 @@ public class Arrays {
java.util.Arrays.hashCode(a);
java.util.Arrays.hashCode((Object[])null);
}
testSort();
}
}