Okay, let's assume that you're
going to be doing internal, sequential sorting, in place, on an array. There are
tons of algorithms to choose from, and although many of them are faster than
others in general, for most sorting algorithms there does exist a situation
where a particular algorithm is a better choice than any other. First of all,
many of the algorithms below make use of “Swap”. This can be a function or a
macro, whose sole purpose is to swap two items from anywhere in memory.
One such definition for this is:
[hide source code]
template <class T>
inline void Swap(T &i1, T &i2) {
const T i3(i1);
i1 = i2;
i2 = i3;
}
I'll also present a few other necessary odds and ends used by some algorithms:
[hide source code]
template
<class T>
bool Sorted(const
T a[],
int n) {
for (int i = n-1; i > 0; --i)
if (a[i] < a[i-1])
return false;
return true;
}
template <class
T>
void Reverse(T
a[],
int n) {
T *l = &a[0], *r = &a[n-1];
while (l < r) {
Swap(*l, *r);
++l; --r;
}
}
template <class
T>
void Shuffle(T a[],
int n, long seed=DEFAULT_SEED) {
long holdrand = seed;
while (n > 0) {
unsigned
int randNo = (((holdrand = holdrand * 214013L + 2531011L) >> 16) &
0x7fff);
const
int pos = randNo % n;
--n;
if (pos != n) Swap(a[pos], a[n]);
}
}
Bubble Sort

Probably the most well-known
sorting algorithm is bubble-sort. In bubble sort, the values float one at a time
to the end of the array, slowly moving one item into position each pass. The
code consists of two loops, a compare, and a swap (of adjacent items). Bubble
Sort is stable because it only ever swaps adjacent items, and only if the first
one is strictly greater than the second one. Sometimes people implement Simple
Sort, thinking they have actually implemented Bubble Sort, but if it doesn't
always compare adjacent items, it isn't Bubble Sort. Depending on the order of
the for-loops this is sometimes called Sink Sort.
Example with random numbers:
Initial list: 17, 37, 48, 29, 13, 42, 50, 4, 63
First outer loop pass (swaps in red, comparisons not resulting a swap are
omitted):
Swap: 17, 37, 29, 48, 13, 42,
50, 4, 63
Swap: 17, 37, 29, 13, 48, 42,
50, 4, 63
Swap: 17, 37, 29, 13, 42, 48,
50, 4, 63
Swap: 17, 37, 29, 13, 42, 48, 4, 50,
63
Second outer loop pass:
Swap: 17, 29, 37, 13, 42, 48, 4,
50, 63
Swap: 17, 29, 13, 37, 42, 48, 4,
50, 63
Swap: 17, 29, 13, 37, 42, 4, 48,
50, 63
Third outer loop pass:
Swap: 17, 13, 29, 37, 42, 4, 48,
50, 63
Swap: 17, 13, 29, 37, 4, 42, 48,
50, 63
Fourth outer loop pass:
Swap: 13, 17, 29, 37, 4, 42, 48,
50, 63
Swap: 13, 17, 29, 4, 37, 42, 48,
50, 63
Fifth outer loop pass:
Swap: 13, 17, 4, 29, 37, 42, 48,
50, 63
Sixth outer loop pass:
Swap: 13, 4, 17, 29, 37, 42, 48,
50, 63
Seventh outer loop pass:
Swap: 4, 13, 17, 29, 37, 42, 48,
50, 63
As you can see, some items take a long time to reach their destination (like the
4 above) because they can only move one position for each outer loop pass. These
are called turtles, and are the main speed problem with Bubble Sort. Items
moving in the other direction move fast (like 48 above) and are called hares.
[hide source code]
template
<class T>
void
BubbleSort(T a[], int n) {
for (int i = n-1; i
> 0; --i) {
for (int j = 0; j < i; ++j) {
if (a[j + 1] < a[j])
Swap(a[j], a[j + 1]);
}
}
}
http://en.wikipedia.org/wiki/Bubble_sort
http://xlinux.nist.gov/dads/HTML/bubblesort.html
Improved Bubble Sort
Bubble sort can be improved by
keeping track of whether or not a swap is made on each inner loop pass, and if not, then
the sorting is completed. This means adding a set, a clear, and a test of a flag
variable to the code. Now, if the array is already sorted or nearly sorted, then
the inner loop only runs once, or only a few times. Improved Bubble Sort is
still stable as it still only swaps adjacent items and only if the first one is
strictly greater than the second one.
[hide source code]
template
<class T>
void
ImprovedBubbleSort(T a[], int n) {
for (int i = n-1; i > 0; --i) {
bool swapped = false;
for (int j = 0; j < i; ++j) {
if (a[j + 1] < a[j]) {
Swap(a[j], a[j + 1]);
swapped = true;
}
}
if (!swapped) break;
}
}
http://www.sorting-algorithms.com/bubble-sort
Best Bubble Sort
The improvement to Bubble Sort above can be taken further. After each pass
we have moved the highest misplaced item to its correct spot. This means that
further bubbles will never need to be bubbled up past this point. If that point
is the first item in the array, then we are done. So this acts much like the
first improved version, but is even better if the last few items are already in
position.
Again, this is still Bubble Sort, and so is stable.
[hide source code]
template <class T, class Size>
void BestBubbleSort(T a[], Size n) {
Size i = n-1;
do {
Size k = 0;
for (Size j = 0; j < i; ++j) {
if (a[j + 1] < a[j]) {
Swap(a[j], a[j + 1]);
k = j;
}
}
i = k;
} while (i > 0);
}
Simple Sort (Substitution Sort)
Perhaps the easiest algorithm to
implement is simple sort. In simple sort, the
code consists of two loops that compare every item to every other item, and a swap
if they are out of order. The inner loop can either start just after the outer
loop, or an extra test can be added to check that the inner loop position is
greater than the outer one.
Unlike Bubble Sort, this algorithm is NOT stable.
[hide source code]
template
<class T>
void
SimpleSort(T a[], int n) {
for (int i = 0; i < n; ++i)
for (int j = i+1; j < n; ++j)
if (a[j] < a[i])
Swap(a[j], a[i]);
}
http://www.sortieralgorithmen.de/simplesort/index.html
Shaker Sort

Also called "Cocktail Sort". There are some algorithms based
on Bubble Sort, which improve the algorithm somewhat. For example Shaker Sort,
which performs Bubble Sort, passes in alternating directions. The alternating
passes mean many items repeatedly move back and forth from one spot, to the next
spot over, and then back to the first spot, giving a noticeable shaking effect
if animated. This allows both small and large items to move towards their
destination, as the algorithm progresses.
Shaker Sort is stable.
[hide source code]
template
<class T>
void
ShakerSort(T a[], int n) {
int j, l = 1, r = n-1, k = r;
do {
for (j = r; j >= l; --j)
if (a[j] < a[j - 1]) {
Swap(a[j - 1], a[j]);
k = j;
}
l = k + 1;
for (j = l; j <= r; ++j)
if (a[j] < a[j - 1]) {
Swap(a[j - 1], a[j]);
k = j;
}
r = k - 1;
}
while (l <= r);
}
http://en.wikipedia.org/wiki/Shaker_sort
http://xlinux.nist.gov/dads/HTML/shakerSort.html
Odd Even Transposition Sort

Also called "Brick sort". Another algorithm is to
alternately swap the even items with their next item for one pass, and then the odd items
with their next item for the next pass. This allows both large and small
items to go towards their destination as we progress too. It is similar to
Shaker Sort and Improved Bubble Sort, but doesn’t have a way of stopping early. The
good news is that the algorithm lends itself well to parallelisation because all
of the compare-and-swap operations in each pass could theoretically be performed at once. Multi-core CPUs could be utilised to do this, but perhaps to very
little advantage since there are many asymptotically faster algorithms anyway.
Odd Even Transposition Sort is stable.
[hide source code]
template
<class T>
void
OddEvenSort(T a[], int n) {
for (int i = 0; i < n; ++i) {
if (i & 1) {
for (int j = 2; j < n; j+=2)
if (a[j] < a[j-1])
Swap(a[j-1], a[j]);
}
else {
for (int j = 1; j < n; j+=2)
if (a[j] < a[j-1])
Swap(a[j-1], a[j]);
}
}
}
http://www.cs.rit.edu/~atk/Java/Sorting/sorting.html
Several-Unique Sort
An interesting variation of
Bubble Sort was obtained by a genetic algorithm known as '"Critticall".
That's right; a computer learned a better algorithm. The change was to make all
items equal to the current bubble, float up with it. This was called
Several-Unique Sort, because it works well when there are several instances of
each item (i.e. duplicates), but it is otherwise slightly slower than bubble
sort.
Several Unique Sort is NOT stable.
[hide source code]
template
<class T>
void
SeveralUniqueSort(T a[], int n) {
T HighValue;
T NewValue;
int EndIndex = n - 1;
int SwapIndex;
int Position;
if (n > 0) {
do {
SwapIndex = 0;
Position = -1;
HighValue = a[0];
while (Position < EndIndex) {
NewValue = a[++Position];
if (NewValue < HighValue) {
a[SwapIndex++] = NewValue;
a[Position] = HighValue;
}
if (HighValue < NewValue) {
SwapIndex = Position;
HighValue = a[Position];
}
}
EndIndex = SwapIndex - 1;
}
while (Position >= SwapIndex);
}
}
http://www.critticall.com/sort.html
Comb Sort
The most effective way to
improve bubble sort is to allow the gap between items compared to vary. Start
off high and then decrease, in the same way that if your hair was really knotted
you wouldn't reach for you finest toothed comb first. You'd probably use a comb
with big teeth first to get out the big knots, and then use a finer comb. This
is exactly what comb sort does.
Many people have tried different
shrink factors other than 1.3 with this algorithm, and there are even lists of
hand-optimised gap tables out there. But I still find that it’s very hard to
beat the factor of 1.3 and the special modification to prevent gaps of nine or
ten (known as combSort11), by any significant amount, and some other gap-tables
are actually slower. The same early-exit trick of improved bubble sort is also
utilised here.
Comb sort is NOT stable.
[hide source code]
template <class T>
while CombSort(T a[], int n) {
bool swapped;
int gap = n;
do {
gap = (gap*10)/13;
switch (gap) {
case 0 : gap = 1; break;
case 9 :
case 10: gap = 11; break;
default: break;
}
swapped = false;
for (int i = gap; i < n; ++i) {
const int j = i - gap;
if (a[i] < a[j]) {
Swap(a[i], a[j]);
swapped = true;
}
}
} while (swapped || (gap > 1));
}
http://en.wikipedia.org/wiki/Comb_sort
http://xlinux.nist.gov/dads/HTML/combSort.html
HBubbleSort
The same technique as is used in
Shell Sort (see further down the page) can be used with Bubble Sort. Each pass consists of a bubble sort
pass, instead of an insertion pass as would be done in Shell Sort. This
algorithm is rarely used as it exists mostly to demonstrate that the principle
used in Shell Sort can be used independently of the H-Sorting algorithm used.
Like Shell Sort,
H-Bubble Sort is NOT stable.
[hide source code]
template <class
T>
void
HBubbleSort(T a[], int n) {
for (int
m = n; m > 0; m = static_cast<int>(floor(m * (1.0/1.33)))) {
for (int i = 0; i < m; ++i) {
int l = i+((n-i-1)/m)*m;
do {
int k = 0;
for (int
j = i; j < l; j+=m) {
if (a[j + m] < a[j]) {
Swap(a[j], a[j + m]);
k = j;
}
}
l = k;
} while (m == 1 && l > i);
}
}
}
http://www.cs.princeton.edu/~rs/shell/paperF.pdf
HShakeSort
The same technique as is used in Shell Sort (see further down the page) can be used with Shaker Sort. Each pass consists of a upwards and a downwards
shaker sort pass, instead of an insertion pass as would be done in Shell Sort.
This algorithm is also rarely used as it exists mostly to demonstrate that the
principle used in Shell Sort can be used independently of the H-Sorting
algorithm used.
Like Shell Sort, H-Shake Sort is NOT stable.
[hide source code]
template
<class T>
void
HShakeSort(T a[], int n) {
for (int m = n;
m > 0; m = static_cast<int>(floor(m * (1.0/1.7)))) {
for (int i = 0; i < m; ++i) {
int j, l = i+m, r = i+((n-i-1)/m)*m, k = r;
do {
for (j = r; j >= l; j-=m)
if
(a[j] < a[j - m]) {
Swap(a[j - m], a[j]);
k = j;
}
l = k + m;
for (j = l; j <= r; j+=m)
if
(a[j] < a[j - m]) {
Swap(a[j - m], a[j]);
k = j;
}
r = k - m;
}
while (m == 1 && l <= r);
}
}
}
http://www.cs.princeton.edu/~rs/shell/paperF.pdf
HBrickSort
The same technique as is used in Shell
Sort (see further down
the page) can be used with Brick Sort.
Each pass consists of an odd and an even brick sort pass, instead of an
insertion pass as would be done in Shell Sort.
This algorithm is also rarely used as it exists mostly to demonstrate that the
principle used in Shell Sort can be used independently of the H-Sorting
algorithm used.
Like Shell Sort, H-Brick Sort is NOT stable.
[hide source code]
template
<class T>
void
HBrickSort(T a[], int n) {
for (int m = n;
m > 0; m = static_cast<int>(floor(m * (1.0/1.22)))) {
for (int i = 0; i < m; ++i) {
bool swapped;
const int m2 = m+m;
do {
swapped = false;
for (int j = i+m; j < n; j+=m2) {
if (a[j] < a[j-m]) {
Swap(a[j-m], a[j]);
swapped = true;
}
}
for (int j = i+m2; j < n; j+=m2) {
if
(a[j] < a[j-m]) {
Swap(a[j-m], a[j]);
swapped = true;
}
}
}
while (m == 1 && swapped);
}
}
}
http://www.cs.princeton.edu/~rs/shell/paperF.pdf
Selection Sort

Another basic sorting strategy
is to simply search for the next largest (or smallest if you prefer) item on
each pass and move it into position. This gives the advantage that we only need
to perform one swap on each pass, making it much better in terms of less
copying, but performing the same number of comparisons. At first one might think
this would make a huge speed difference, but in reality it often doesn't make that
much difference. This algorithm is called Selection Sort.
Selection Sort on an array is NOT stable because in order to
place an item in its final spot, we move the item that was there to somewhere
that might be before some other item that was equal to it, thus reversing the
order of items that were equal (if you follow).
[hide source code]
template
<class T>
void
SelectionSort(T a[], int n) {
for (int i = n-1; i > 0; --i) {
int nmax = 0;
for (int j = i; j > 0; --j)
if (a[nmax] < a[j])
nmax = j;
Swap(a[i], a[nmax]);
}
}
http://en.wikipedia.org/wiki/Selection_sort
http://xlinux.nist.gov/dads/HTML/selectionSort.html
Dual Selection Sort
In an effort to further reduce
copying, and reduce the number of passes, you can of course search for the
next smallest and next largest item on each pass, and move both into position.
There is a special case you have to deal with though, when
the largest item is already where you want the smallest one to go. Even with the
attempted improvements, it most
likely not going to be significantly faster than standard selection sort. Dual Selection sort on an array
is also NOT stable.
[hide source code]
template
<class T>
void
DualSelectionSort(T a[], int n) {
int i = 0, nmin, nmax;
while (i < --n) {
nmin = nmax = i;
for (int j = n; j > i; --j) {
if (a[j] < a[nmin])
nmin = j;
else if (a[nmax] < a[j])
nmax = j;
}
Swap(a[nmin], a[i]);
if (nmax == i)
Swap(a[nmin], a[n]);
else
Swap(a[nmax], a[n]);
++i;
}
}
http://warp.povusers.org/DoubleBurstSelectionSort/
http://www.softpanorama.org/Algorithms/Sorting/selection_sort.shtml
Bingo Sort
As with Several Unique Sort, we
can write a selection style algorithm, which will take advantage of there being
many duplicates. After finding the largest item, you perform another pass to
move any other items equal to the max item to their final place. This means less
passes than Selection Sort if there are on average more than 2 of each item,
otherwise the extra loops only slows things down.
Bingo Sort on an array is NOT
stable for the same reason as Selection Sort.
[hide source code]
template
<class T>
void
BingoSort(T a[], int n) {
int i = n-1;
while (i > 0) {
int nmax = 0, j;
for (j = i; j > 0; --j)
if (a[nmax] < a[j])
nmax = j;
Swap(a[i], a[nmax]);
nmax = i--;
j = i;
while (j > 0) {
--j;
if (a[nmax] == a[j]) {
Swap(a[i], a[j]);
--i;
}
}
}
}
http://xlinux.nist.gov/dads/HTML/bingosort.html
Exact Sort
Exact Sort is a form of
Selection Sort with a difference. Usually after finding the nth smallest item
you swap it with the nth item in the array. However the item that was previously
at the nth position gets moved to some other location, which probably isn't its
final resting position. Consequently each item ends up getting moved almost
twice on average. This algorithm goes to the extreme to further reduce
unnecessary item copying. In this algorithm, before an item is placed in its
final resting position, the one that was in its spot is first relocated to its
final resting position, and before that, the one there must be moved to its
final resting position, and before that… well you get the picture. Suffice to
say, each item is only moved once. This is except of course, for the item that
is removed first and put back at the end, so that you have room to move the rest
around. One would only want to use this algorithm if moving an item was
extremely expensive (slow) for some reason. E.g. you might do this when
rearranging furniture. First work out the way or ordering things using the least
moves, and then follow that plan.
Exact sort is NOT stable.
[hide source code]
template <class T>
while ExactSort(T a[], int n) {
int *const b = new int[n];
for (int i = 0; i<n; ++i) b[i] = false;
int sp = realIndex(b, 0, n);
int t = sp;
T tv =a[t];
for (;;) {
int c = 0;
for (int i = n; --i >= 0;)
if (!b[i] && i != sp && a[i] < tv) ++c;
int d = realIndex(b, c, n);
if (d == -1) break;
b[d] = true;
if (d == sp) {
if (d != t) a[d] = tv;
sp = realIndex(b, 0, n);
if (sp == -1) break;
tv = a[sp];
t = sp;
} else {
Swap(a[d], tv);
t = d;
}
}
delete[] b;
}
int realIndex(const int b[], int f, int n) {
int fc = -1;
for (int i = 0; i<n; ++i) {
if (!b[i]) ++fc;
if (fc == f) return i;
}
return -1;
}
http://www.geocities.com/p356spt/
Insertion Sort

Another well known sorting
technique is Insertion Sort. During Insertion Sort, you always have a sorted
portion and an unsorted portion. Initially the sorted portion consists of only 1
item. Each subsequent step involves finding a place in the
sorted portion for a single item from the unsorted portion, and inserting that
item into the sorted portion. Thus enlarging the sorted portion and shrinking
the unsorted portion, until the unsorted portion becomes empty. At that point,
the entire thing is sorted. An unfortunate
downside to this technique is that Inserting the item usually means moving many
items over to make room for it. Usually the search starts from the position just
to the right of where the unsorted item lies, such that if the array is already
sorted, it is very fast to insert. I prefer to first test if the item to be
inserted actually needs any moving at all, and if not (i.e. it comes after all
items in the sorted portion), we simply extend the sorted portion to include
this item, and don't move anything. In fact, this is among the fastest
algorithms for sorting an almost sorted array. When it is completely sorted,
there are O(n) compares and no swaps. That's the same amount of work as testing
if an array is sorted already! However if the array starts off in reverse
order, it will be very slow because many moves and many comparisons will be
required. Note: Moving items over to make
room for insertion, can be done by means of repeated swapping; however I prefer
to simply remember the value to insert, move the required items all over by one, and
then place the remembered item into the hole.
Insertion sort is stable.
[hide source code]
template
<class T>
void
InsertionSort(T a[], int n) {
for (int i = 1; i < n; ++i) {
if (a[i] < a[i - 1]) {
int j = i - 1;
const T tempItem = a[i];
do {
a[j + 1] = a[j];
--j;
}
while ((j >= 0) && (tempItem < a[j]));
a[j + 1] = tempItem;
}
}
}
http://en.wikipedia.org/wiki/Insertion_sort
http://xlinux.nist.gov/dads/HTML/insertionSort.html
Dual Insertion Sort
Insertion sort, like bubble sort
can be improved in various ways. The main problem with it is that it takes so
long to move items over in memory to make space for the item to be inserted. One
(not so effective) way to reduce this is to store two sorted portions, on at the
end of the array, and one at the beginning. This means that one you've worked
out which end to insert into; there are half as many moves required for
inserting it. As things progress, having to move on average half the number of
items to perform an insert begins to halve the time taken to sort. However the
size of the sorted portions needs to occasionally be balanced as well, by
performing an extra swap occasionally to transfer items from the end of the
larger sorted portion onto the smaller portion. For a large number of items this
modified algorithm
might cut the time taken by perhaps almost 50%, which is not that fantastic
since a large array can be sorted much faster using other techniques anyway. You
could say that this 'optimisation' is
beating a dead horse,
but in a way it is a step towards the Library Sort algorithm mentioned further
on.
As far as I am aware, this
algorithm is my own creation. Therefore I do not yet have any links to other
sites with the algorithm. Furthermore its usefulness is extremely low anyway, as
it is rather large and not significantly faster than insertion sort.
[hide source code]
template
<class T>
void
DualInsertionSort(T a[], int n) {
int p = 0, q = n-1;
while (p < q) {
if (p < n-1-q) {
if ((q < n-1) && (a[q] < a[p])) {
Swap(a[p + 1], a[q]);
++q;
}
if (a[p + 1] < a[p]) {
int j = p;
const T tempItem = a[p + 1];
do {
a[j + 1] = a[j];
--j; }
while ((j >= 0) && (tempItem < a[j])); a[j + 1] = tempItem;
}
++p;
}
else {
if ((p > 0) && (a[q] < a[p])) {
Swap(a[p], a[q - 1]);
--p;
}
if (a[q] < a[q - 1]) {
int j = q;
const T tempItem = a[q - 1];
do {
a[j - 1] = a[j];
++j;
}
while ((j < n) && (a[j] < tempItem));
a[j - 1] = tempItem;
}
--q;
}
}
}
Binary Insertion Sort
Another way to speed insertion
sort up a
little, is to perform a binary search to find the place to insert. Although most
of the time is spent moving items along to make space, the searching phase for
each item can be significantly improved (especially over the reverse sorted
case) by performing a binary search. The trade-off is that the already sorted
case will be slower, because it will take n*log(n) compares instead of just n, but the average case will improve somewhat.
Binary
Insertion sort can be stable if written carefully, as shown.
[hide source code]
template
<class T>
void
BinaryInsertionSort(T a[], int n) {
for (int i = 1; i < n; ++i) {
int l = 0, r = i, j = i-1;
const T tempItem = a[i];
while (l < r) {
const int m = (l
+ r)>>1;
if (tempItem < a[m])
r = m;
else
l = m + 1;
}
while (j >= l) {
a[j + 1] = a[j];
--j;
}
a[l] = tempItem;
}
}
http://en.wikipedia.org/wiki/Insertion_sort
http://xlinux.nist.gov/dads/HTML/binaryinsort.html
Interpolation Insertion Sort
Another way to search for the
insertion point is by successive approximations using
interpolation search. Basically
you calculate about where the insert should go, and then see if your estimate
was too high or too low and make another linearly interpolated guess. This is very much like
binary searching, but can often be faster. However this also requires that the
items at the very least, also support being subtracted. That means Either being
a built-in type, or having a subtraction operator defined in addition to the
less-than operator. This is therefore not a comparison-based sorting algorithm.
Interpolation Insertion sort can also
be stable if written carefully, as shown.
[hide source code]
template
<class T>
void
InterpInsertionSort(T a[], int n) {
for (int i = 1; i < n; ++i) {
int l = 0, r = i-1, j = r, m;
const T tempItem = a[i];
while (l <= r) {
const int range
= int(a[r] - a[l]);
if (range > 0) {
m =
int(l + ((tempItem-a[l])*(r-l))/range);
if (m < l)
m = l;
else if (m > r)
m = r;
}
else
m = l;
if (tempItem < a[m])
r = m - 1;
else
l = m + 1;
}
while (j >= l) {
a[j + 1] = a[j];
--j;
}
a[l] = tempItem;
}
}
Strand Sort
Another way to improve upon the
insertion technique is to identify runs of items in the array, and insert all of
the items in the run in the same pass. This is the idea behind Strand sort.
First measure the length of the ascending sequence, and then make a pass through
the sorted portion to insert those items. The insertion pass for the run is
actually the same as an in-place merge, which we'll come across later.
Strand
sort is stable.
[hide source code]
template <class
T>
void StrandSort(T a[], int n) {
int m, r;
if (n > 1) {
r = 0;
do {
m = r;
do ++r; while ((r < n) && !(a[r] < a[r-1]));
InPlaceMerge(a, 0, m, r);
} while (r < n);
}
}
http://xlinux.nist.gov/dads/HTML/strandSort.html
Bidirectional Strand Sort
We can attempt to further
enhance strand sort by allowing it to recognise reverse sorted strands too, flip those around
then insert/merge them as per strand sort. This usually boosts the average
sequence length. You could again say that this 'optimisation' is
beating a dead horse
though.
Bidirectional Strand Sort is stable if you only look for explicitly decreasing
sequences before reversing them, as done here.
As far as I am aware, this
algorithm is my own creation. Therefore I do not yet have any links to other
sites with the algorithm. Furthermore its usefulness is extremely low anyway.
[hide source code]
template
<class T>
void
BidirectionalStrandSort(T a[], int n) {
int m, r;
if (n > 1) {
r = 0;
do {
m = r++;
if (r < n) {
if (a[r] < a[r-1]) {
do ++r; while
((r < n) && (a[r] < a[r-1]));
Reverse(&a[m], r-m);
}
else {
do ++r; while
((r < n) && !(a[r] < a[r-1]));
}
}
InPlaceMerge(a, 0, m, r);
}
while (r < n);
}
}
Order Sort
This algorithm is kind of a cross between
array-based Insertion Sort and list-based Insertion Sort, that attempts to keep
the advantages of both.
It uses an index list internally, allowing insertion into the sorted portion (of
the index list) in constant time. However unlike list-based insertion sort, it
can find the point of insertion in faster than O(n) time. This is done by
sampling through the original array over some of the items that have already
been inserted. Instead of examining half of them on average, it explicitly scans
the square root of the number inserted so far at even intervals, and uses this as a best guess.
From there it scans linearly forwards to find the real insertion point. Of m
items already inserted, this
samples sqrt(m) items initially, plus on average sqrt(m)/2 further items to find
the real insertion point, assuming the initial sampling gave a close
approximation. This
leads to a typical execution time of about O(n1.5).
Just like list-based Insertion Sort, Order Sort is stable.
[hide source code]
template <class
T>
void OrderSort(T a[], int n) {
T *c = new T[n];
T HighestBelow, HighestEntry, LowestEntry;
int *b = new int[n];
int x, y, EntryStep;
int HighestBelowPos, CurrentPosition, Buffer1, Buffer2 = 0;
int HighestSeen = 0, LowestSeen = 0;
for (x=0; x < n; ++x)
{
const T EntryValue = a[x];
bool IsLower = false;
bool IsHigher = false;
if (x==0 || a[x] < LowestEntry)
{
IsLower = true;
b[x] = LowestSeen;
LowestEntry = EntryValue;
LowestSeen = x;
}
if (x==0 || !(a[x] < HighestEntry))
{
IsHigher = true;
b[HighestSeen] = x;
b[x] = 0;
HighestEntry = EntryValue;
HighestSeen = x;
}
if (!IsLower && !IsHigher)
{
EntryStep = 1 + (int)floor(sqrt((double)x));
HighestBelowPos = n;
for (y=0; y < x; y += EntryStep)
{
if (HighestBelow < a[y] && a[y] < EntryValue)
{
HighestBelow = a[y];
HighestBelowPos = y;
}
}
if (HighestBelowPos == n)
{
HighestBelow = LowestEntry;
HighestBelowPos = LowestSeen;
}
bool IsNotGreater = true;
CurrentPosition = HighestBelowPos;
do {
Buffer1 = CurrentPosition;
CurrentPosition = b[CurrentPosition];
if (EntryValue < a[CurrentPosition])
{
IsNotGreater = false;
Buffer2 = CurrentPosition;
}
} while (IsNotGreater);
b[Buffer1] = x;
b[x] = Buffer2;
}
}
CurrentPosition = LowestSeen;
for (x=0; x < n; ++x)
{
c[x] = a[CurrentPosition];
CurrentPosition = b[CurrentPosition];
}
for (x=0; x < n; ++x)
a[x] = c[x];
delete[] b;
delete[] c;
}
http://www.ddj.com/dept/cpp/184402000
Shell Merge Sort
Perhaps the best way to improve
insertion sort is the same way we made comb sort such a huge improvement over
bubble sort. We allow the sorted arrays to consist of items that are very spread
out. I've called this Shell Merge Sort, because it has similarities to both
Shell Sort and Merge Sort. In fact it performs basically the same as the original Shell
Sort which halved the increment size after each pass, except that the order of
the passes is different.
This sorting algorithm is NOT stable.
I have not seen Shell Sort
written like this elsewhere. Therefore I do not yet have any links to other
sites with the algorithm. Furthermore it has no advantage over normal Shell
Sort, which does not use recursion.
[hide source code]
template
<class T>
void
ShellMerge(T a[], int n,
int d) {
for (int i = d; i < n; i+=d) {
if (a[i] < a[i - d]) {
int j = i - d;
const T tempItem = a[i];
do {
a[j + d] = a[j];
j -= d;
}
while ((j >= 0) && (tempItem < a[j]));
a[j + d] =
tempItem;
}
}
}
template
<class T>
void
ShellMergeSortAux(T a[], const
int n, const
int d) {
if (d < n) {
ShellMergeSortAux(a, n, d*2);
ShellMergeSortAux(&a[d], n-d, d*2);
ShellMerge(a, n, d);
}
}
template
<class T>
void
ShellMergeSort(T a[], int n) {
ShellMergeSortAux(a, n, 1);
}
Shell Sort

The other way to improve
insertion sort is of course, Shell Sort invented by Donald Shell in 1959.
Whereby the same principle as Comb Sort is used, except that the insertion
method is used to k-sort the items on each pass. There are many variants of this
algorithm alone, but basically they all just use different number sequences for
the gap sizes in each pass. The Incerpj - Sedgewick sequence used here is
currently regarded as the best and is about O(nlog2n), but there are a lot of other gap table
variations out there. Of algorithms that use no extra heap or stack memory
(recursive calls) this is among the fastest.
This sorting algorithm is NOT
stable.
[hide source code]
template
<class T>
void
ShellSort(T a[], int n) {
const int incs[] =
{
1, 3, 7, 21, 48, 112,
336, 861, 1968, 4592, 13776,
33936, 86961, 198768, 463792, 1391376,
3402672, 8382192, 21479367, 49095696, 114556624,
343669872, 52913488, 2085837936};
for (int l =
sizeof(incs)/sizeof(incs[0]); l > 0;) {
const int m = incs[--l];
for (int i = m; i < n; ++i) {
int j = i - m;
if (a[i] < a[j]) {
const T tempItem = a[i];
do {
a[j+m] = a[j];
j-=m;
}
while ((j >= 0) && (tempItem < a[j]));
a[j+m] = tempItem;
}
}
}
}
http://xlinux.nist.gov/dads/HTML/shellsort.html
http://www.research.att.com/~njas/sequences/A036569
http://www.cs.fiu.edu/~weiss/Shellsort.html
Hybrid Comb Sort
Comb Sort (mentioned at the end
of the bubble-sort section) can be further sped up by switching to Insertion
Sort when the comparison gap becomes small enough. This is called Hybrid Comb
sort.
This sorting algorithm is NOT stable.
[hide source code]
template
<class T>
void
HybridCombSort(T a[], int n) {
int gap = n;
while (gap > 8) {
gap = (gap*10)/13;
for (int i = gap; i < n; ++i) {
const int j = i - gap;
if (a[i] < a[j])
Swap(a[i], a[j]);
}
}
InsertionSort(a, n);
}
http://yagni.com/combsort/correspondence.php
Library Sort
There have been some recent
developments which expand on the insertion method of sorting, such as Library
Sort. The idea expands on Insertion Sort, and attempts to get around the problem
of requiring so many moves to insert each item. It leaves holes in the target array so that
it doesn't don't have
to move many items when doing the insert. When the number of holes gets low, a
special pass is done to spread the items out some more again. This implementation was created from a
description of the algorithm, as I have never actually seen another
implementation of it. As such, it might not be 100% as the authors (Michael
A Bender, Martin Farach-Colton, Miguel Mosteiro 2006)
intended it, but nevertheless it works well enough, even though this
implementation is rather
large.
The below implementation of this algorithm is NOT stable, though it should be
able to be modified to be stable
[hide source code]
template <class T>
while LibrarySort(T a[], int n) {
if (n > 1) {
const double E = 1.25;
const int maxNumElems = int(floor(n*E));
const int MyNULL = int(-1);
int nnn = 0;
int* items = new int[maxNumElems];
for (int i = 0; i < maxNumElems; ++i)
items[i] = MyNULL;
for (int phase = 1; phase <= n; phase<<=1) {
int numElems = int(floor((phase*2-1)*E));
if (numElems > maxNumElems)
numElems = maxNumElems;
for (int k = 0; k < phase; ++k) {
int l = 0, r = numElems, j;
const int tempItem = nnn;
while (l < r) {
int m =
FindNonNullMidpointItem(items, l, r);
if (m == MyNULL) break;
if (a[items[m]] < a[tempItem])
l = m + 1;
else
r = m;
}
j = l;
while (items[j] != MyNULL && j < numElems)
++j;
if (j == numElems) {
j = l--;
do {
--j;
} while (items[j] != MyNULL && j > 0);
while (j < l) {
items[j] = items[j+1];
++j;
}
} else {
while (j > l) {
items[j] = items[j-1];
--j;
}
}
items[l] = tempItem;
if (++nnn == n)
break;
}
double numElems2 = (phase*4-3)*E;
if (numElems2 <= maxNumElems) {
for (; numElems2 >= 0; numElems2-=E*2) {
while (numElems >= 0 && items[numElems] == MyNULL)
--numElems;
if (numElems < 0)
break;
const int thePos = int(floor(numElems2));
if (items[thePos] == MyNULL) {
items[thePos] = items[numElems];
items[numElems] = MyNULL;
}
--numElems;
}
}
}
int iFrom=0, iTo=0;
while (iFrom < maxNumElems) {
if (items[iFrom] != MyNULL)
items[iTo++] = items[iFrom];
++iFrom;
}
ReorderAccordingTo(a, n, items);
delete[] items;
}
}
int FindNonNullMidpointItem(const int items[], int l, int r) {
const int MyNULL = ~0;
const int m = (l + r)>>1;
int mPlusOff = m, mOff = 0;
do {
if (items[mPlusOff] != MyNULL)
return mPlusOff;
mOff = (mOff >= 0) ? -mOff-1 : -mOff;
mPlusOff = m + mOff;
} while (mPlusOff >= l && mPlusOff < r);
return MyNULL;
}
while ReorderAccordingTo(T a[], int n, int indexes[]) {
for (int i=0; i<n; ++i) {
if (indexes[i] != i) {
const T tempItem = a[i];
int l = i, j;
while ((j = indexes[l]) != i) {
a[l] = a[j];
indexes[l] = l;
l = j;
}
a[l] = tempItem;
indexes[l] = l;
}
}
}
http://en.wikipedia.org/wiki/Library_sort
http://www.cs.sunysb.edu/~bender/pub/FUN2004-librarysort.ps
In-Place Merge Sort
Another way to sort is by using
a technique known as merging. Using the process of induction, we can write a
divide-and-conquer
algorithm that splits the problem up into two smaller sequences, sorts those
using the same algorithm and then merges those two to produce a
larger sorted list, merely by removing the first item from the sequence with the
lowest item each time and appending it onto the sorted sequence.
In-Place Merge
Sort is stable.
[hide source code]
template <class
T>
void Rotate(T a[], int n, const
int amount) {
int num = n, dest = 0, source = n-amount;
int start = dest;
T tempItem = a[dest];
for (;;) {
--num;
a[dest] = a[source];
dest = source;
source -= amount;
if (source < 0) source += n;
if (source == start) {
a[dest] = tempItem;
if (--num <= 0) break;
if (--dest < 0) dest += n;
if (--source < 0) source += n;
start = dest;
tempItem = a[dest];
}
}
}
template <class
T>
void InPlaceMerge(T a[], const
int l, const int m, const
int r) {
int i = l-1, j = m;
while (j < r) {
do ++i; while ((i < j) && !(a[j] < a[i]));
if (i >= j) break;
const
int oldj = j;
do ++j; while ((j < r) && (a[j] < a[i]));
const
int num = j-oldj;
Rotate(&a[i], j-i, num);
i += num;
}
}
template <class T>
void InPlaceMergeSortAux(T a[], const int l, const int r) {
if (r - l > 1)
{
const int m = (l + r)>>1;
InPlaceMergeSortAux(a, l, m);
InPlaceMergeSortAux(a, m, r);
InPlaceMerge(a, l, m, r);
}
}
template <class T>
void InPlaceMergeSort(T a[], int n) {
InPlaceMergeSortAux(a, 0, n);
}
http://www.cs.ubc.ca/spider/harrison/Java/sorting-demo.html
Bitonic Merge Sort
Unfortunately it doesn't turn
out to be very easy to efficiently merge data 'in-place' in an array. We have a few
options though. We could sort the second half in reverse, which at first seems
counter-productive, but
this novel idea allows us to actually sort it much faster without using extra
memory. Note that despite what you may find
another website saying, this idea only works for array sizes that are a power of
two. The modification made by some to pretend that it is a power of two in size,
but with the imaginary elements before the start all being equal, does not work.
I have emailed the author of one site about this, showing that even in their own
applet some cases will fail, but I have got no response. A common solution is to
perform an Insertion Sort pass over the array afterwards if the size was not a
power of two, as it will be mostly in order.
[hide source code]
template <class
T>
void
BitonicMerge(T a[], int lo,
int cnt, bool
ascend) {
while (cnt > 1) {
cnt >>= 1;
for (int i=((lo>0)
? lo : 0); i<lo+cnt; ++i)
if (ascend == (a[i+cnt] < a[i]))
Swap(a[i], a[i+cnt]);
BitonicMerge(a, lo, cnt, ascend);
lo += cnt;
}
}
template <class
T>
void
BitonicSortAux(T a[], int lo,
int cnt, bool
ascend) {
if (cnt > 1) {
const int k = cnt >> 1;
BitonicSortAux(a, lo, k, true);
BitonicSortAux(a, lo+k, k, false);
BitonicMerge(a, lo, cnt, ascend);
}
}
template <class
T>
void
BitonicSort(T a[], int n) {
int PowerOfTwo = 1;
while (PowerOfTwo < n) PowerOfTwo<<=1;
BitonicSortAux(a, n-PowerOfTwo, PowerOfTwo, true);
if ((n&(n-1)) != 0) InsertionSort(a, n);
}
http://xlinux.nist.gov/dads/HTML/bitonicSort.html
http://www.tools-of-computing.com/tc/CS/Sorts/bitonic_sort.htm
Merge Sort
Although Merge Sort can be
implemented without using extra memory, the merging process is either slow
or complicated that way. A much faster or easier way to merge is to use extra
memory for the merges. Merge Sort using a second
buffer as a temporary only. Or better yet, merge from one
buffer into another, then merge back again, as I've done below. Merge Sort is stable.
[hide source code]
template <class T>
while MergeSort(T a[], int n) {
T *const b = new T[n];
MergeSortAtoA(a, b, 0, n);
delete[] b;
}
template <class T>
while MergeSortAtoA(T a[], T b[], const int l, const int r) {
if (r - l > 1) {
const int m = (l + r)>>1;
MergeSortAtoB(a, b, l, m);
MergeSortAtoB(a, b, m, r);
MergeAtoB(b, a, l, m, r);
}
}
template <class T>
while MergeSortAtoB(T a[], T b[], const int l, const int r) {
if (r - l > 1) {
const int m = (l + r)>>1;
MergeSortAtoA(a, b, l, m);
MergeSortAtoA(a, b, m, r);
MergeAtoB(a, b, l, m, r);
} else if (r - l == 1) {
b[l] = a[l];
}
}
template <class T>
while MergeAtoB(const T a[], T b[], const int l, const int m, const int r) {
int i = l, j = m, c = l;
while (i < m && j < r)
b[c++] = (a[j] < a[i]) ? a[j++] : a[i++];
while (i < m) b[c++] = a[i++];
while (j < r) b[c++] = a[j++];
}
Breadth-First Merge Sort

Merge Sort is fairly recursive,
and obviously it would be nice to not use so much stack space. With some effort
we can translate the algorithm to its iterative 'breadth-first' form. However,
doing this it seems quite difficult at first to deal with arrays that are not a
power of two in size. No Problem, we just pretend it is a power of two, but
ignore any items outside the actual range. We then treat the array as n
sequences of length 1. We merge odd and even sub-arrays to produce n/2 sequences
of length 2. Then we again merge odd and even sub-arrays to produce n/4
sequences of length 4, etc. Until finally we merge 2 sequences of length n/2 to
complete the sorting.
Breadth-First Merge Sort is stable.
[hide source code]
template <class T>
while BreadthFirstMergeSort(T a[], int n) {
T *const b = new T[n];
int powerOfTwo=1;
while (powerOfTwo < n) powerOfTwo<<=1;
int lo = n-powerOfTwo, step = 1;
while (step < powerOfTwo) {
int val = lo;
while (val < n) {
int l = val; val += step;
const int m = val; val += step;
if (m > 0) {
const int r = val;
if (l < 0) l = 0;
MergeAtoA(a, b, l, m, r);
}
}
step<<=1;
}
delete[] b;
}
template <class T>
while MergeAtoA(T a[], T b[], const int l, const int m, const int r) {
int i = l, j = m, c = l;
while (i < m && j < r)
b[c++] = (a[j] < a[i]) ? a[j++] : a[i++];
int d = c;
while (i < m) a[d++] = a[i++];
for (int k = l; k < c; ++k)
a[k] = b[k];
}
Breadth-First Bitonic Merge Sort

We could have used the same
'breadth-first' idea just mentioned back when we were doing bitonic merges too.
Build tiny increasing and decreasing sequences and use bitonic merge on those until you have a
single bitonic sequence, and then make that in to the first half of a bitonic
sequence, conveniently known as a monotonic (i.e. sorted) sequence.
[hide source code]
template
<class T>
void
BreadthBitonicSort(T a[], int n) {
int PowerOfTwo = 1, lo, cnt, start;
while (PowerOfTwo < n)
PowerOfTwo<<=1;
lo = n-PowerOfTwo;
for (int step =
2; step <= PowerOfTwo; step<<=1) {
cnt = step;
do {
cnt >>= 1;
start = lo;
do {
if (start >= 0)
if ((((start-lo) & step) == 0) == (a[start+cnt] < a[start]))
Swap(a[start], a[start+cnt]);
if (((++start-lo) & cnt) != 0) start += cnt;
}
while (start+cnt < n);
}
while (cnt > 1);
}
if ((n&(n-1)) != 0) InsertionSort(a, n);
}
http://www.tools-of-computing.com/tc/CS/Sorts/bitonic_sort.htm
Semi-Breadth-First Bitonic Merge Sort
When first attempting to write
this in its breadth-first form, I only made the inner loop breadth-first, not
the outer one. This gives yet another distinct sorting pattern, and actually
seemed to run faster than either of the other forms, so I kept it.
[hide source code]
template
<class T>
void
SemiBreadthBitonicSort(T a[], int n) {
int PowerOfTwo = 1;
while (PowerOfTwo < n) PowerOfTwo<<=1;
int lo = n-PowerOfTwo;
for (int step=2;
step <= PowerOfTwo; step<<=1) {
int val = lo;
bool ascend = true;
do {
int cnt = step, finish = val+step;
do {
cnt >>= 1;
int start = val, mid = val+cnt;
do {
if (start >= 0)
if (ascend == (a[start+cnt] < a[start]))
Swap(a[start], a[start+cnt]);
if (++start >= mid) {
start += cnt;
mid = start+cnt;
}
}
while (start+cnt < finish);
}
while (cnt > 1);
val = finish;
ascend = !ascend;
}
while (val < n);
}
if ((n&(n-1)) != 0) InsertionSort(a, n);
}
Natural Merge Sort
A different approach is to take
advantage of and existing runs in the data, just like Strand sort did, and merge
whatever runs happen to already be present in the sequence, rather than breaking
it up into sequences at odd places. This is done by allocating extra memory
twice the size of the original array. We logically split this extra memory into
two buffers and are then free to dump runs of items in either buffer as appropriate. For
every item from the input list that you examine, you copy the item into one of
the output buffers according to the following rules:
1. If both of the next items in the
input buffers are larger than the last item added to the output buffer: Append the one with the larger top value,
keeping the average of the top values as low as possible, giving maximum chance
of being able to further extend the run with the next item added.
2. If both of the next items in the input buffers are smaller than the last item
added to the output buffer:
Again, append it to the one with the larger top value, keeping the average of
the top values as low as possible, making it more likely that the next item will
be able to once again further extend the run.
3. If one of the next items in the input buffers is less than the last item
added to the output buffer, and the other is greater: Append the one with the
larger top value because that extends the run length whereas appending the one
from the other buffer would not. This is important because
otherwise run lengths will not increase very quickly and the sort would actually
become O(n2).
Natural Merge Sort is NOT stable.
[hide source code]
template <class
T>
void NaturalMerge(T *a, T *q[2], const
int top[2]) {
int idx[2] = {0}, i = 0;
int dir = (q[1][0] < q[0][0]) ? 1 : 0;
a[i++] = q[dir][idx[dir]++];
while (idx[dir] < top[dir]) {
dir = (q[1][idx[1]] < q[0][idx[0]]) ? 1 : 0;
if (q[dir][idx[dir]] < a[i-1] && a[i-1] < q[1-dir][idx[1-dir]])
dir = 1-dir;
a[i++] = q[dir][idx[dir]++];
}
dir = 1-dir;
while (idx[dir] < top[dir]) {
a[i++] = q[dir][idx[dir]++];
}
}
template <class
T>
void NaturalMergeSort(T a[],
int n) {
if (n < 2) return;
int idx[2], i;
T *q[2];
q[0] = new T[2 * n];
q[1] = &q[0][n];
for (;;) {
idx[0] = idx[1] = i = 0;
int half = 0;
q[0][idx[0]++] = a[i++];
while (i < n) {
if (a[i] < q[half][idx[half]-1])
half = 1-half;
q[half][idx[half]++] = a[i++];
}
if (idx[1] == 0)
break;
NaturalMerge(a, q, idx);
}
delete[] q[0];
}
http://www.eternallyconfuzzled.com/tuts/algorithms/jsw_tut_sorting.aspx#merge
Continue with more Array Sorting >
Download Mega Sorting
Demo program (104933 bytes zip file)