/* SortsOfSorters
* By: John P. Spurgeon
* Updated: 21 April 2018
* - Bug fix: Instance variable swapCount needs to be reset in sort method.
* - Change: Removed the prefix "actually" from method names.
* At: http://scribbledecobble.blogspot.com/2018/04/sorts-of-sorters.html
*
* Classic Sequences to Sort
*
* 1. A Multiset
* Sequence: c a b d d a b d a d
* See: TAOCP, vol. 3, sec. 5.1.2
*
* 2. A Set
* Sequence: 503 087 512 061 908 170 897 275 653 426 154 509 612 677 765 703
* See: TAOCP, vol. 3 (2nd ed.), p. 76
*/
// With a little help from my friends...
import java.util.List;
import java.util.LinkedList;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.Collections;
/* Counters */
interface ComparisonCounter
{
int getComparisonCount();
}
interface ExchangeCounter
{
int getExchangeCount();
}
// meter: n. a device that measures and records the quantity, degree, or rate of something.
interface SortMeter extends ComparisonCounter, ExchangeCounter
{
boolean countsComparisons();
boolean countsExchanges();
}
// Note: lhs => left hand side, rhs => right hand side.
final class CountingComparator<E> implements Comparator<E>, ComparisonCounter
{
private Comparator<E> cmp;
private int comparisonCount = 0;
public CountingComparator(Comparator<E> cmp)
{
this.cmp = cmp;
}
public final int compare(E lhs, E rhs)
{
comparisonCount++;
return cmp.compare(lhs, rhs);
}
public final int getComparisonCount()
{
return comparisonCount;
}
}
/* abstracted adj.
*
* 1. Drawn off; separate, apart from 1660.
* 2. Withdrawn from the contemplation of present objects; absent in mind 1643.
* *3. Separated from the concrete, ideal, abstruse (replaced by ABSTRACT a. 4) - 1823.
*
* The Evil one a. stood From his own evil MILT.
*
* * disinterestedness.
*
* Source: The Sorter Oxford English Dictionary on Historical Principles,
* Third Edition, Revised with Addenda (1947).
*/
class Sorters
{
public static final String
ABSTRACTED_SORTER = "Abstracted Sorter",
ABSTRACTED_EXCHANGE_SORTER = "Abstracted Exchange Sorter",
BAD_BUBBLE_SORTER = "Bad Bubble Sorter",
BETTER_BUBBLE_SORTER = "Better Bubble Sorter",
BEST_BUBBLE_SORTER = "Best Bubble Sorter",
COLLECTIONS_SORTER = "Collections Sorter",
COMPARISON_COUNTING_SORTER = "Comparison Counting Sorter",
COMPARISON_EXCHANGE_COUNTING_SORTER = "Comparison/Exchange Counting Sorter",
INSERTION_SORTER = "Insertion Sorter",
MERGE_SORTER = "Merge Sorter",
QUICK_SORTER = "Quick Sorter";
}
/* Abstract Sorters */
abstract class Sorter implements SortMeter
{
protected abstract <E> void sortMethod(List<E> sequence, Comparator<E> cmp);
public <E> void sort(List<E> sequence, Comparator<E> cmp)
{
this.sortMethod(sequence, cmp);
}
public final <E extends Comparable<E>> void sort(List<E> sequence)
{
Comparator<E> cmp = (E lhs, E rhs) -> lhs.compareTo(rhs);
sort(sequence, cmp);
}
public final <E extends Comparable<E>> void sortReverse(List<E> sequence)
{
Comparator<E> cmp = (E lhs, E rhs) -> rhs.compareTo(lhs);
sort(sequence, cmp);
}
// Sorter is abstracted (withdrawn from the contemplation of present objects),
// because although it implements the SortMeter interface, it doesn't actually
// count comparisons or exchanges; it has a habitually forgetful disposition.
public boolean countsComparisons() { return false; }
public boolean countsExchanges() { return false; }
public int getComparisonCount() { return -1; }
public int getExchangeCount() { return -1; }
public String toString()
{
return Sorters.ABSTRACTED_SORTER;
}
}
abstract class ComparisonCountingSorter extends Sorter
{
private int comparisonCount = 0;
public final <E> void sort(List<E> sequence, Comparator<E> cmp)
{
CountingComparator<E> cc = new CountingComparator<E>(cmp);
this.sortMethod(sequence, cc);
comparisonCount = cc.getComparisonCount();
}
public final boolean countsComparisons()
{
return true;
}
public final int getComparisonCount()
{
return comparisonCount;
}
public String toString()
{
return Sorters.COMPARISON_COUNTING_SORTER;
}
}
abstract class ExchangeSorter extends Sorter
{
protected <E> void swap(List<E> sequence, int i, int j)
{
E tmp = sequence.get(i);
sequence.set(i, sequence.get(j));
sequence.set(j, tmp);
}
public String toString()
{
return Sorters.ABSTRACTED_EXCHANGE_SORTER;
}
}
abstract class ComparisonExchangeCountingSorter extends ExchangeSorter
{
private int comparisonCount = 0, swapCount = 0;
protected final <E> void swap(List<E> sequence, int i, int j)
{
super.swap(sequence, i, j);
swapCount++;
}
public final <E> void sort(List<E> sequence, Comparator<E> cmp)
{
swapCount = 0;
CountingComparator<E> cc = new CountingComparator<E>(cmp);
this.sortMethod(sequence, cc);
comparisonCount = cc.getComparisonCount();
}
public final boolean countsComparisons()
{
return true;
}
public final int getComparisonCount()
{
return comparisonCount;
}
public final boolean countsExchanges()
{
return true;
}
public final int getExchangeCount()
{
return swapCount;
}
public String toString()
{
return Sorters.COMPARISON_EXCHANGE_COUNTING_SORTER;
}
}
/* Bubble Sort */
class BadBubbleSorter extends ComparisonExchangeCountingSorter
{
// Exercise 1a: Fix the problem with the bubble sort implementation below.
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
/*
* This method sorts. But it sorts in the wrong direction!
*/
final int n = sequence.size();
final int lastIndex = n - 1;
for (int count = 0; count < n; count++)
{
for (int i = 0; i < lastIndex; i++)
{
int j = i + 1;
E lhs = sequence.get(i);
E rhs = sequence.get(j);
if (cmp.compare(lhs, rhs) < 0) swap(sequence, i, j);
}
}
}
public String toString()
{
return Sorters.BAD_BUBBLE_SORTER;
}
}
class BetterBubbleSorter extends BadBubbleSorter
{
// Exercise 1b: Implement bubble sort again. Make it better this time.
// Specifically: On each pass, do one fewer comparison than before.
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
final int n = sequence.size();
int lastIndex = n - 1;
for (int count = 0; count < n; count++)
{
for (int i = 0; i < lastIndex; i++)
{
int j = i + 1;
E lhs = sequence.get(i);
E rhs = sequence.get(j);
if (cmp.compare(lhs, rhs) > 0) swap(sequence, i, j);
}
lastIndex--;
}
}
public String toString()
{
return Sorters.BETTER_BUBBLE_SORTER;
}
}
final class BestBubbleSorter extends BetterBubbleSorter
{
// Exercise 1c: Implement bubble sort one more time. Make it even better.
// Specifically: Stop as soon as you know you're done.
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
final int n = sequence.size();
int lastIndex = n - 1;
for (int count = 0; count < n; count++)
{
int exchangeCount = getExchangeCount();
for (int i = 0; i < lastIndex; i++)
{
int j = i + 1;
E lhs = sequence.get(i);
E rhs = sequence.get(j);
if (cmp.compare(lhs, rhs) > 0) swap(sequence, i, j);
}
if (getExchangeCount() == exchangeCount) break;
lastIndex--;
}
}
public final String toString()
{
return Sorters.BEST_BUBBLE_SORTER;
}
}
/* Insertion Sort */
final class InsertionSorter extends ComparisonExchangeCountingSorter
{
/* Sorting an unordered sequence. */
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
// Exercise 2a: Implement insertion sort.
final int n = sequence.size();
for (int i = 1; i < n; i++)
{
for (int j = i; j > 0; j--)
{
int k = j - 1;
E rhs = sequence.get(j);
E lhs = sequence.get(k);
if (cmp.compare(lhs, rhs) > 0)
swap(sequence, k, j);
}
}
}
/* Inserting into a sorted multiset. */
public <E> void insertIntoMultiset(E element, List<E> sortedSeq, Comparator<E> cmp)
{
// Exercise 2b: Write code that inserts an element into a multiset (duplicates allowed).
// Assume that the list passed to the method is in sorted order.
int i = 0;
for (E nextElement : sortedSeq)
{
if (cmp.compare(element, nextElement) < 0)
i++;
else if (cmp.compare(element, nextElement) >= 0)
break;
}
sortedSeq.add(i, element);
}
public final <E extends Comparable<E>>
void insertIntoMultiset(E element, List<E> sortedSeq)
{
Comparator<E> cmp = (E lhs, E rhs) -> lhs.compareTo(rhs);
insertIntoMultiset(element, sortedSeq, cmp);
}
public final <E extends Comparable<E>>
void insertIntoMultisetReverse(E element, List<E> sortedSeq)
{
Comparator<E> cmp = (E lhs, E rhs) -> rhs.compareTo(lhs);
insertIntoMultiset(element, sortedSeq, cmp);
}
/* Inserting into a sorted set. */
public <E> boolean insertIntoSet(E element, List<E> sortedSeq, Comparator<E> cmp)
{
// Exercise 2c: Write code that inserts an element into a set (duplicates not allowed).
// Assume that the list passed to the method is in sorted order.
int i = 0;
for (E nextElement : sortedSeq)
{
if (cmp.compare(element, nextElement) < 0)
i++;
else if (cmp.compare(element, nextElement) == 0)
return false;
else
break;
}
sortedSeq.add(i, element);
return true;
}
public final <E extends Comparable<E>>
boolean insertIntoSet(E element, List<E> sortedSeq)
{
Comparator<E> cmp = (E lhs, E rhs) -> lhs.compareTo(rhs);
insertIntoSet(element, sortedSeq, cmp);
return false;
}
public final <E extends Comparable<E>>
boolean insertIntoSetReverse(E element, List<E> sortedSeq)
{
Comparator<E> cmp = (E lhs, E rhs) -> rhs.compareTo(lhs);
insertIntoSet(element, sortedSeq, cmp);
return false;
}
public String toString()
{
return Sorters.INSERTION_SORTER;
}
}
/* Quick Sort */
final class QuickSorter extends ComparisonExchangeCountingSorter
{
// lower is inclusive, upper is exclusive
private <E> void quickSort(List<E> sequence, Comparator<E> cmp, int lower, int upper)
{
if (upper - lower <= 1)
return;
int pivot = lower;
final E pivotValue = sequence.get(pivot);
int j = pivot + 1;
for (int i = j; i < upper; i++)
{
final int compareResult = cmp.compare(sequence.get(i), pivotValue);
if (compareResult <= 0)
{
if (i > j)
swap(sequence, i, j);
if (compareResult < 0)
swap(sequence, pivot, j);
pivot = j++;
}
}
quickSort(sequence, cmp, lower, pivot);
quickSort(sequence, cmp, j, upper);
}
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
// Exercise 3: Implement quick sort.
quickSort(sequence, cmp, 0, sequence.size());
}
public String toString()
{
return Sorters.QUICK_SORTER;
}
}
/* Merge Sort */
final class MergeSorter extends ComparisonCountingSorter
{
private <E> void merge(List<E> list, Comparator<E> cmp, int lower, int middle, int upper)
{
final int n = upper - lower;
List<E> buffer = new ArrayList<E>(n);
int i, j, k;
for (i = 0; i < n; i++)
buffer.add(null);
i = lower; j = middle; k = 0;
while (i < middle && j < upper)
{
int compareResult = cmp.compare(list.get(i), list.get(j));
if (compareResult <= 0)
buffer.set(k++, list.get(i++));
if (compareResult >= 0)
buffer.set(k++, list.get(j++));
}
while (i < middle)
buffer.set(k++, list.get(i++));
while (j < upper)
buffer.set(k++, list.get(j++));
for (i = 0; i < n; i++)
list.set(lower + i, buffer.get(i));
}
private <E> void mergeSort(List<E> list, Comparator<E> cmp, int lower, int upper)
{
final int n = upper - lower;
if (n <= 1)
return;
int middle = lower + (n / 2);
mergeSort(list, cmp, lower, middle);
mergeSort(list, cmp, middle, upper);
merge(list, cmp, lower, middle, upper);
}
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
// Exercise 4: Implement merge sort.
mergeSort(sequence, cmp, 0, sequence.size());
}
public String toString()
{
return Sorters.MERGE_SORTER;
}
}
/* A sorter that uses Java's Collections.sort method to sort. */
final class CollectionsSorter extends ComparisonCountingSorter
{
protected <E> void sortMethod(List<E> sequence, Comparator<E> cmp)
{
Collections.sort(sequence, cmp);
}
public String toString()
{
return Sorters.COLLECTIONS_SORTER;
}
}
/* String Utilities */
class StringTester
{
public static boolean isNumeric(String s)
{
try
{
double d = Double.parseDouble(s);
}
catch(NumberFormatException nfe)
{
return false;
}
return true;
}
public static boolean areNumeric(String[] strings)
{
if (strings == null || strings.length == 0)
return false;
else
for (String s : strings)
if (!isNumeric(s))
return false;
return true;
}
}
class StringSorter {
public static List<Double> sortAsNumbers(String[] strings, Sorter sorter) {
List<Double> list = new LinkedList<Double>();
for (String s : strings) list.add(Double.parseDouble(s));
sorter.sort(list);
return list;
}
public static List<Double> sortAsNumbersReverse(String[] strings, Sorter sorter) {
List<Double> list = new ArrayList<Double>();
for (String s : strings) list.add(Double.parseDouble(s));
sorter.sortReverse(list);
return list;
}
public static List<String> sortAsStrings(String[] strings, Sorter sorter) {
List<String> list = new ArrayList<String>();
for (String s : strings) list.add(s);
sorter.sort(list);
return list;
}
public static List<String> sortAsStringsReverse(String[] strings, Sorter sorter) {
List<String> list = new LinkedList<String>();
for (String s : strings) list.add(s);
sorter.sortReverse(list);
return list;
}
public static List<Double> insertNumbersIntoSet(String[] strings, InsertionSorter sorter) {
List<Double> list = new LinkedList<Double>();
for (String s : strings) sorter.insertIntoSet(Double.parseDouble(s), list);
return list;
}
public static List<Double> insertNumbersIntoMultiset(String[] strings, InsertionSorter sorter) {
List<Double> list = new ArrayList<Double>();
for (String s : strings) sorter.insertIntoMultiset(Double.parseDouble(s), list);
return list;
}
public static List<String> insertStringsIntoSet(String[] strings, InsertionSorter sorter) {
List<String> list = new ArrayList<String>();
for (String s : strings) sorter.insertIntoSet(s, list);
return list;
}
public static List<String> insertStringsIntoMultiset(String[] strings, InsertionSorter sorter) {
List<String> list = new LinkedList<String>();
for (String s : strings) sorter.insertIntoMultiset(s, list);
return list;
}
}
/* Main Program: SortsOfSorters */
public class SortsOfSorters
{
private static <E> void print(List<E> sequence, SortMeter counter, String label)
{
System.out.println(counter);
System.out.print(label + ":");
for (E element : sequence) System.out.print(" " + element);
System.out.println();
if (counter.countsComparisons())
System.out.println("Comparisons: " + counter.getComparisonCount());
if (counter.countsExchanges())
System.out.println("Exchanges: " + counter.getExchangeCount());
System.out.println();
}
public static void main(String[] args)
{
Sorter[] sortsOfSorters = {
new BadBubbleSorter(), new BetterBubbleSorter(), new BestBubbleSorter(),
new InsertionSorter(), new MergeSorter(), new QuickSorter(),
new CollectionsSorter(),
};
if (StringTester.areNumeric(args))
{
for (Sorter sorter : sortsOfSorters)
{
print(StringSorter.sortAsNumbers(args, sorter), sorter, "Sorted");
print(StringSorter.sortAsNumbersReverse(args, sorter), sorter, "Reversed");
if (sorter.toString().equals(Sorters.INSERTION_SORTER))
{
InsertionSorter iSrtr = (InsertionSorter) sorter;
print(StringSorter.insertNumbersIntoSet(args, iSrtr), iSrtr, "Set");
print(StringSorter.insertNumbersIntoMultiset(args, iSrtr), iSrtr, "Multiset");
}
}
}
else
{
for (Sorter sorter : sortsOfSorters)
{
print(StringSorter.sortAsStrings(args, sorter), sorter, "Sorted");
print(StringSorter.sortAsStringsReverse(args, sorter), sorter, "Reversed");
if (sorter.toString().equals(Sorters.INSERTION_SORTER))
{
InsertionSorter iSrtr = (InsertionSorter) sorter;
print(StringSorter.insertStringsIntoSet(args, iSrtr), iSrtr, "Set");
print(StringSorter.insertStringsIntoMultiset(args, iSrtr), iSrtr, "Multiset");
}
}
}
}
}