By Marc Clifton
After my last article on a QuickSort algorithm with customizable swapping, Jonathan de Halleux made some improvements to my original design and added some sorting algorithms. As I was writing this article and waiting for Jonathan's blessing, Robert Rohde provided some additional sorting algorithms which I have included here as well. Rather than updating the original article, I felt it would be better to represent Jonathan's and Robert's work separately from my original article, as they have contributed so much that it is quite a different beast. My contribution at this point is the article itself, some additions to the
ISwap interface, and integrating both Jonathan's and Robert's work into a unified namespace. All kudos should go to Jonathan's and Robert's excellent work--I learned a few things writing this article!
This library provides a very nice and flexible package of sorting algorithms from which the developer can choose. The algorithms presented here have been ported to C# and are based on selected algorithms in Java found here. Click on the pictures to run an applet that shows the algorithm running!
The sorting algorithms are:
- Bidirectional Bubble Sort
- Bubble Sort
- Double Storage Merge Sort (utilizes setter)
- Fast Quick Sort (utilizes setter)
- Heap Sort
- In Place Merge Sort (utilizes setter)
- Insertion Sort (utilizes setter)
- Odd-Even Transport Sort
- Quick Sort
- Quick Sort With Bubble Sort (utilizes setter)
- Selection Sort
- Shaker Sort
- Shear Sort
- Shell Sort
Sorting algorithms such as InPlaceMergeSort, InsertionSort, and ShellSort perform set operations rather than swap operations. For this reason, the ISwap interface includes two "Set" methods. If you aren't using one of the algorithms that uses a setter, then you can ignore them.
All of these sort algorithms are thread safe.
The Object Model
The object model easily allows for additional sorting algorithms to be added. The following UML diagram illustrates this with the FastQuickSorter implementation:
Additional sorters are derived from
The following illustrates some of the key implementation features of the NSort library.
Two interfaces provide the necessary abstraction of concrete instances.
public interface ISorter
void Sort(IList list);
This interface abstracts the concrete sorting algorithms.
public interface ISwap
void Swap(IList array, int left, int right);
void Set(IList array, int left, int right);
void Set(IList array, int left, object obj);
This interface abstracts the concrete swapper. In some cases, additional work needs to be done when swapping elements of the array. Defining your own swapping algorithm derived from the
interface allows you to accomplish this work.
This is the abstract base class for all the sorting algorithms. It implements the management of the concrete
ISorter instances. Each concrete sorting algorithm implements two constructors--a default constructor and a constructor in which you can specify your own comparer and swapper functions. In the
SwapSorter class, these are implemented as follows:
this.comparer = new ComparableComparer();
this.swapper = new DefaultSwap();
As you can see from the default constructor above, the default comparer and swapper are implemented.
public SwapSorter(IComparer comparer, ISwap swapper)
if (comparer == null)
throw new ArgumentNullException("comparer");
throw new ArgumentNullException("swapper");
this.comparer = comparer;
this.swapper = swapper;
The above code illustrates specifying your own comparer and swapper.
An instance of the default comparer or swapper can also be passed in, should you only need to specify your own instance of one or the other, but not both.
The default swapper implementation is straight-forward:
public class DefaultSwap : ISwap
public void Swap(IList array, int left, int right)
public void Set(IList array, int left, int right)
public void Set(IList array, int left, object obj)
The default comparer very powerful:
public class ComparableComparer : IComparer
public int Compare(IComparable x, Object y)
#region IComparer Members
int IComparer.Compare(Object x, Object y)
Notice in this implementation, the
IComparer.Compare method invokes the object's
ICompareTo function. This has the advantage of putting the comparison "smarts" into the object being compared against. There are several advantages to this. If the object is a class, only certain fields in the class might be involved in the comparison. Also, the object itself can determine what other types of objects it can be compared with and provide necessary translation/conversion services. Given this flexibility, overriding the comparer is probably only necessary when comparing third party objects that do not implement
CompareTo or in cases where you wish to extend the native
The usage is best illustrated by looking at the NUnit-compatible unit tests that are provided with the download.
Instantiating The Sorter
Each sorting algorithm has it's own test fixture:
public class QuickSorterTest : SorterTest
public void SetUp()
this.Sorter = new QuickSorter();
Creating, Sorting, And Verifying Some Sample Data
The test, verifying that the sorter is behaving correctly:
public void SortInt()
Random rnd = new Random();
int list = new int;
for(i = 0; i<list.Length; ++i)
list[i] = rnd.Next();
SortedList sl = new SortedList();
foreach(int key in list)
i = 0;
foreach(int val in sl.Keys)
The code includes unit tests written for Marc's unit test framework. In the code included in this article, the unit tests are part of the NSort assembly.
Speaking of which, beware of the fast quicksort algorithm--the unit test fail on this once, but since the test data is randomly generated, it hasn't been reproduce!
Jonathan has written an excellent performance benchmark framework that will readily integrate into Marc's unit test framework. The next step is to do so, and illustrate that effort by benchmarking the various sorting algorithms.