Chapter 17: Elementary Sorting Algorithms
17.05Beginner / Intermediate5 topics20 min5 interactive animations

Other educational sorts

These sorts are rarely used in production, but each one isolates a useful idea: sorting in both directions, moving far-apart elements early, insertion done with swaps, diminishing gaps, and minimising writes.

Learning goals

  • Trace cocktail shaker, comb, gnome, shell and cycle sort
  • Explain the specific weakness of bubble or insertion sort each one targets
  • Know their complexity and stability

Prerequisites

Bubble sortInsertion sort
17.05.1

Cocktail shaker

Bubble sort moves large values right quickly but small values left by only one step per pass. These slow small values are called turtles. Cocktail shaker sort alternates a forward pass and a backward pass, so turtles also move quickly.

Try the “Turtle at the end” preset, where 1 sits in the last slot: bubble sort needs all 6 passes (21 comparisons), while cocktail sort needs one round trip plus a final check pass (15 comparisons).

Best

O(n)

Average

O(n²)

Worst

O(n²)

Space

O(1)

Cocktail shaker sort

Interactive · edit the input

Your input

Try

int[] arr · length 7

38
27
43
3
9
82
10
0123456
Being comparedMovedSorted
Setup

Bubble sort in both directions: forward passes push large values right, backward passes pull small values left.

Step 1 / 46
Java
1public static void cocktailSort(int[] arr) {
2 int start = 0, end = arr.length - 1;
3 boolean swapped = true;
4 while (swapped) {
5 swapped = false;
6 for (int i = start; i < end; i++) { // forward pass
7 if (arr[i] > arr[i + 1]) { swap(arr, i, i + 1); swapped = true; }
8 }
9 if (!swapped) break;
10 end--;
11 swapped = false;
12 for (int i = end - 1; i >= start; i--) { // backward pass
13 if (arr[i] > arr[i + 1]) { swap(arr, i, i + 1); swapped = true; }
14 }
15 start++;
16 }
17}

Live state

0 comparisons0 swaps

start

0

end

6

No condition evaluated on this step
Execution trace (dry run table)Show 46 steps
StepLineStateWhat happens
1L2start=0 end=6Bubble sort in both directions: forward passes push large values right, backward passes pull small values left.
2L6start=0 end=6 swapped=falseForward pass.
3L7start=0 end=6 i=0Out of order: swap.
4L7start=0 end=6 i=0Swapped.
5L7start=0 end=6 i=1In order.
6L7start=0 end=6 i=2Out of order: swap.
7L7start=0 end=6 i=2Swapped.
8L7start=0 end=6 i=3Out of order: swap.
9L7start=0 end=6 i=3Swapped.
10L7start=0 end=6 i=4In order.
11L7start=0 end=6 i=5Out of order: swap.
12L7start=0 end=6 i=5Swapped.
13L10start=0 end=5The largest remaining value is fixed. end = 5.
14L12start=0 end=5 swapped=falseBackward pass.
15L13start=0 end=5 i=4Out of order: swap.
16L13start=0 end=5 i=4Swapped.
17L13start=0 end=5 i=3In order.
18L13start=0 end=5 i=2In order.
19L13start=0 end=5 i=1Out of order: swap.
20L13start=0 end=5 i=1Swapped.
21L13start=0 end=5 i=0Out of order: swap.
22L13start=0 end=5 i=0Swapped.
23L15start=1 end=5 swapped=trueThe smallest remaining value is fixed. start = 1.
24L6start=1 end=5 swapped=falseForward pass.
25L7start=1 end=5 i=1In order.
26L7start=1 end=5 i=2Out of order: swap.
27L7start=1 end=5 i=2Swapped.
28L7start=1 end=5 i=3Out of order: swap.
29L7start=1 end=5 i=3Swapped.
30L7start=1 end=5 i=4In order.
31L10start=1 end=4The largest remaining value is fixed. end = 4.
32L12start=1 end=4 swapped=falseBackward pass.
33L13start=1 end=4 i=3In order.
34L13start=1 end=4 i=2In order.
35L13start=1 end=4 i=1Out of order: swap.
36L13start=1 end=4 i=1Swapped.
37L15start=2 end=4 swapped=trueThe smallest remaining value is fixed. start = 2.
38L6start=2 end=4 swapped=falseForward pass.
39L7start=2 end=4 i=2Out of order: swap.
40L7start=2 end=4 i=2Swapped.
41L7start=2 end=4 i=3In order.
42L10start=2 end=3The largest remaining value is fixed. end = 3.
43L12start=2 end=3 swapped=falseBackward pass.
44L13start=2 end=3 i=2In order.
45L15start=3 end=3 swapped=falseThe smallest remaining value is fixed. start = 3.
46L17Sorted with 21 comparisons, 11 swaps.
17.05.2

Comb sort

Comb sort compares elements a gap apart, starting with a gap of about n / 1.3 and shrinking it by 1.3 each pass. Large gaps remove turtles early, and the final gap-1 passes are bubble passes on nearly sorted data.

It is not stable, because long-distance swaps can reorder equal keys.

Best

O(n log n)

Worst

O(n²)

Space

O(1)

Comb sort

Interactive · edit the input

Your input

Try

int[] arr · length 7

38
27
43
3
9
82
10
0123456
Being comparedMovedSorted
Setup

Like bubble sort, but compare elements a gap apart. Large gaps move small values from the end quickly.

Step 1 / 37
Java
1public static void combSort(int[] arr) {
2 int gap = arr.length;
3 boolean sorted = false;
4 while (!sorted) {
5 gap = Math.max(1, (int) (gap / 1.3)); // shrink factor 1.3
6 if (gap == 1) sorted = true; // last pass is bubble-like
7 for (int i = 0; i + gap < arr.length; i++) {
8 if (arr[i] > arr[i + gap]) {
9 swap(arr, i, i + gap);
10 sorted = false;
11 }
12 }
13 }
14}

Live state

0 comparisons0 swaps

gap

7

sorted

false

No condition evaluated on this step
Execution trace (dry run table)Show 37 steps
StepLineStateWhat happens
1L2gap=7 sorted=falseLike bubble sort, but compare elements a gap apart. Large gaps move small values from the end quickly.
2L5gap=5 sorted=falsegap = 5.
3L8gap=5 sorted=false i=0In order.
4L8gap=5 sorted=false i=1Out of order across the gap: swap.
5L9gap=5 sorted=false i=1Swapped.
6L5gap=3 sorted=falsegap = 3.
7L8gap=3 sorted=false i=0Out of order across the gap: swap.
8L9gap=3 sorted=false i=0Swapped.
9L8gap=3 sorted=false i=1Out of order across the gap: swap.
10L9gap=3 sorted=false i=1Swapped.
11L8gap=3 sorted=false i=2In order.
12L8gap=3 sorted=false i=3Out of order across the gap: swap.
13L9gap=3 sorted=false i=3Swapped.
14L5gap=2 sorted=falsegap = 2.
15L8gap=2 sorted=false i=0In order.
16L8gap=2 sorted=false i=1In order.
17L8gap=2 sorted=false i=2Out of order across the gap: swap.
18L9gap=2 sorted=false i=2Swapped.
19L8gap=2 sorted=false i=3In order.
20L8gap=2 sorted=false i=4Out of order across the gap: swap.
21L9gap=2 sorted=false i=4Swapped.
22L5gap=1 sorted=truegap = 1. With gap 1 this pass is a bubble pass; if it makes no swaps we are done.
23L8gap=1 sorted=true i=0In order.
24L8gap=1 sorted=true i=1In order.
25L8gap=1 sorted=true i=2In order.
26L8gap=1 sorted=true i=3In order.
27L8gap=1 sorted=true i=4In order.
28L8gap=1 sorted=true i=5Out of order across the gap: swap.
29L9gap=1 sorted=false i=5Swapped.
30L5gap=1 sorted=truegap = 1. With gap 1 this pass is a bubble pass; if it makes no swaps we are done.
31L8gap=1 sorted=true i=0In order.
32L8gap=1 sorted=true i=1In order.
33L8gap=1 sorted=true i=2In order.
34L8gap=1 sorted=true i=3In order.
35L8gap=1 sorted=true i=4In order.
36L8gap=1 sorted=true i=5In order.
37L14Sorted with 23 comparisons, 7 swaps.
17.05.3

Gnome sort

A gnome looks at the pot beside it: if the two are in order it steps forward, otherwise it swaps them and steps back. It is insertion sort written as a single loop with swaps.

It is stable and adaptive, with the same O(n²) worst case as insertion sort but more writes.

Best

O(n)

Average

O(n²)

Worst

O(n²)

Space

O(1)

Gnome sort

Interactive · edit the input

Your input

Try

int[] arr · length 7

38
27
43
3
9
82
10
0123456
Being comparedMovedSorted
Setup

A gnome walks forward while pots are in order and steps back after each swap.

Step 1 / 60
Java
1public static void gnomeSort(int[] arr) {
2 int i = 0;
3 while (i < arr.length) {
4 if (i == 0 || arr[i - 1] <= arr[i]) {
5 i++; // step forward
6 } else {
7 swap(arr, i, i - 1);
8 i--; // step back
9 }
10 }
11}

Live state

0 comparisons0 swaps

i

0

No condition evaluated on this step
Execution trace (dry run table)Show 60 steps
StepLineStateWhat happens
1L2i=0A gnome walks forward while pots are in order and steps back after each swap.
2L4i=0In order, step forward.
3L5i=1i = 1.
4L4i=1Out of order, swap and step back.
5L7i=1Swapped.
6L4i=0In order, step forward.
7L5i=1i = 1.
8L4i=1In order, step forward.
9L5i=2i = 2.
10L4i=2In order, step forward.
11L5i=3i = 3.
12L4i=3Out of order, swap and step back.
13L7i=3Swapped.
14L4i=2Out of order, swap and step back.
15L7i=2Swapped.
16L4i=1Out of order, swap and step back.
17L7i=1Swapped.
18L4i=0In order, step forward.
19L5i=1i = 1.
20L4i=1In order, step forward.
21L5i=2i = 2.
22L4i=2In order, step forward.
23L5i=3i = 3.
24L4i=3In order, step forward.
25L5i=4i = 4.
26L4i=4Out of order, swap and step back.
27L7i=4Swapped.
28L4i=3Out of order, swap and step back.
29L7i=3Swapped.
30L4i=2Out of order, swap and step back.
31L7i=2Swapped.
32L4i=1In order, step forward.
33L5i=2i = 2.
34L4i=2In order, step forward.
35L5i=3i = 3.
36L4i=3In order, step forward.
37L5i=4i = 4.
38L4i=4In order, step forward.
39L5i=5i = 5.
40L4i=5In order, step forward.
41L5i=6i = 6.
42L4i=6Out of order, swap and step back.
43L7i=6Swapped.
44L4i=5Out of order, swap and step back.
45L7i=5Swapped.
46L4i=4Out of order, swap and step back.
47L7i=4Swapped.
48L4i=3Out of order, swap and step back.
49L7i=3Swapped.
50L4i=2In order, step forward.
51L5i=3i = 3.
52L4i=3In order, step forward.
53L5i=4i = 4.
54L4i=4In order, step forward.
55L5i=5i = 5.
56L4i=5In order, step forward.
57L5i=6i = 6.
58L4i=6In order, step forward.
59L5i=7i = 7.
60L11Sorted with 26 comparisons, 11 swaps. Gnome sort is insertion sort done with swaps.
17.05.4

Shell sort intro

Shell sort runs insertion sort on elements gap apart, then shrinks the gap. With gap h, the array becomes h-sorted, meaning every h-th element forms a sorted list. The final gap of 1 is plain insertion sort on nearly sorted data, which is fast.

The gap sequence decides the complexity. Shell's original n/2, n/4, … has an O(n²) worst case. Knuth's (3^k - 1)/2 sequence (1, 4, 13, 40, …) gives O(n^1.5).

Best

O(n log n)

Worst

O(n²) with n/2 gaps

Space

O(1)

Shell sort

Interactive · edit the input

Your input

Try

int[] arr · length 7

38
27
43
3
9
82
10
0123456
Key in handBeing comparedMovedSorted
Setup

Insertion sort on elements gap apart, with the gap halving each round. Far-away elements move early.

Step 1 / 48
Java
1public static void shellSort(int[] arr) {
2 int n = arr.length;
3 for (int gap = n / 2; gap > 0; gap /= 2) {
4 for (int i = gap; i < n; i++) { // gapped insertion sort
5 int temp = arr[i];
6 int j = i;
7 while (j >= gap && arr[j - gap] > temp) {
8 arr[j] = arr[j - gap];
9 j -= gap;
10 }
11 arr[j] = temp;
12 }
13 }
14}

Live state

0 comparisons0 shifts

n

7

No condition evaluated on this step
Execution trace (dry run table)Show 48 steps
StepLineStateWhat happens
1L2n=7Insertion sort on elements gap apart, with the gap halving each round. Far-away elements move early.
2L3gap=3gap = 3: the array is split into 3 interleaved sub-lists.
3L5gap=3 i=3 temp=3 j=3temp = 3. Insert it into its gap-3 sub-list.
4L7gap=3 i=3 temp=3 j=3Shift the larger element gap positions right.
5L8gap=3 i=3 temp=3 j=3arr[3] = arr[0].
6L7gap=3 i=3 temp=3 j=0Stop shifting.
7L11gap=3 i=3 temp=3 j=0arr[0] = 3.
8L5gap=3 i=4 temp=9 j=4temp = 9. Insert it into its gap-3 sub-list.
9L7gap=3 i=4 temp=9 j=4Shift the larger element gap positions right.
10L8gap=3 i=4 temp=9 j=4arr[4] = arr[1].
11L7gap=3 i=4 temp=9 j=1Stop shifting.
12L11gap=3 i=4 temp=9 j=1arr[1] = 9.
13L5gap=3 i=5 temp=82 j=5temp = 82. Insert it into its gap-3 sub-list.
14L7gap=3 i=5 temp=82 j=5Stop shifting.
15L11gap=3 i=5 temp=82 j=5arr[5] = 82.
16L5gap=3 i=6 temp=10 j=6temp = 10. Insert it into its gap-3 sub-list.
17L7gap=3 i=6 temp=10 j=6Shift the larger element gap positions right.
18L8gap=3 i=6 temp=10 j=6arr[6] = arr[3].
19L7gap=3 i=6 temp=10 j=3Stop shifting.
20L11gap=3 i=6 temp=10 j=3arr[3] = 10.
21L3gap=1gap = 1: the array is split into 1 interleaved sub-lists.
22L5gap=1 i=1 temp=9 j=1temp = 9. Insert it into its gap-1 sub-list.
23L7gap=1 i=1 temp=9 j=1Stop shifting.
24L11gap=1 i=1 temp=9 j=1arr[1] = 9.
25L5gap=1 i=2 temp=43 j=2temp = 43. Insert it into its gap-1 sub-list.
26L7gap=1 i=2 temp=43 j=2Stop shifting.
27L11gap=1 i=2 temp=43 j=2arr[2] = 43.
28L5gap=1 i=3 temp=10 j=3temp = 10. Insert it into its gap-1 sub-list.
29L7gap=1 i=3 temp=10 j=3Shift the larger element gap positions right.
30L8gap=1 i=3 temp=10 j=3arr[3] = arr[2].
31L7gap=1 i=3 temp=10 j=2Stop shifting.
32L11gap=1 i=3 temp=10 j=2arr[2] = 10.
33L5gap=1 i=4 temp=27 j=4temp = 27. Insert it into its gap-1 sub-list.
34L7gap=1 i=4 temp=27 j=4Shift the larger element gap positions right.
35L8gap=1 i=4 temp=27 j=4arr[4] = arr[3].
36L7gap=1 i=4 temp=27 j=3Stop shifting.
37L11gap=1 i=4 temp=27 j=3arr[3] = 27.
38L5gap=1 i=5 temp=82 j=5temp = 82. Insert it into its gap-1 sub-list.
39L7gap=1 i=5 temp=82 j=5Stop shifting.
40L11gap=1 i=5 temp=82 j=5arr[5] = 82.
41L5gap=1 i=6 temp=38 j=6temp = 38. Insert it into its gap-1 sub-list.
42L7gap=1 i=6 temp=38 j=6Shift the larger element gap positions right.
43L8gap=1 i=6 temp=38 j=6arr[6] = arr[5].
44L7gap=1 i=6 temp=38 j=5Shift the larger element gap positions right.
45L8gap=1 i=6 temp=38 j=5arr[5] = arr[4].
46L7gap=1 i=6 temp=38 j=4Stop shifting.
47L11gap=1 i=6 temp=38 j=4arr[4] = 38.
48L14Sorted with 15 comparisons, 7 shifts. The final gap-1 pass is plain insertion sort on nearly sorted data, which is fast.
17.05.5

Cycle sort intro

Cycle sort counts how many elements are smaller than an item to find its exact final position, writes it there, picks up the displaced value, and repeats until the cycle closes.

Every value is written at most once into its final slot, so the total writes are at most n. This makes it theoretically optimal in writes, which suits memory where writes are costly. It is O(n²) time and not stable.

Best

O(n²)

Average

O(n²)

Worst

O(n²)

Space

O(1)

Cycle sort

Interactive · edit the input

Your input

Try

int[] arr · length 7

38
27
43
3
9
82
10
0123456
CandidateBeing comparedMovedSorted
Setup

Each value is written directly to its final position. Cycle sort minimises memory writes.

Step 1 / 30
Java
1public static int cycleSort(int[] arr) {
2 int writes = 0;
3 for (int start = 0; start < arr.length - 1; start++) {
4 int item = arr[start];
5 int pos = start;
6 for (int i = start + 1; i < arr.length; i++) if (arr[i] < item) pos++;
7 if (pos == start) continue; // already in place
8 while (item == arr[pos]) pos++; // skip duplicates
9 int tmp = arr[pos]; arr[pos] = item; item = tmp;
10 writes++;
11 while (pos != start) { // rotate the rest of the cycle
12 pos = start;
13 for (int i = start + 1; i < arr.length; i++) if (arr[i] < item) pos++;
14 while (item == arr[pos]) pos++;
15 tmp = arr[pos]; arr[pos] = item; item = tmp;
16 writes++;
17 }
18 }
19 return writes;
20}

Live state

0 comparisons0 writes

writes

0

No condition evaluated on this step
Execution trace (dry run table)Show 30 steps
StepLineStateWhat happens
1L2writes=0Each value is written directly to its final position. Cycle sort minimises memory writes.
2L4start=0 item=38Pick up item = 38.
3L6start=0 item=38 pos=44 values after start are smaller than 38, so 38 belongs at index 4.
4L9start=0 item=9 pos=4Write into arr[4]. Now holding the displaced value 9.
5L13start=0 item=9 pos=11 value after start is smaller than 9, so 9 belongs at index 1.
6L15start=0 item=27 pos=1Write into arr[1]. Holding 27.
7L13start=0 item=27 pos=33 values after start are smaller than 27, so 27 belongs at index 3.
8L15start=0 item=3 pos=3Write into arr[3]. Holding 3.
9L13start=0 item=3 pos=00 values after start are smaller than 3, so 3 belongs at index 0.
10L15start=0 item=38 pos=0The cycle closes back at start.
11L4start=1 item=9Pick up item = 9.
12L6start=1 item=9 pos=10 values after start are smaller than 9, so 9 belongs at index 1.
13L7start=1 item=9 pos=19 is already in place.
14L4start=2 item=43Pick up item = 43.
15L6start=2 item=43 pos=53 values after start are smaller than 43, so 43 belongs at index 5.
16L9start=2 item=82 pos=5Write into arr[5]. Now holding the displaced value 82.
17L13start=2 item=82 pos=64 values after start are smaller than 82, so 82 belongs at index 6.
18L15start=2 item=10 pos=6Write into arr[6]. Holding 10.
19L13start=2 item=10 pos=20 values after start are smaller than 10, so 10 belongs at index 2.
20L15start=2 item=43 pos=2The cycle closes back at start.
21L4start=3 item=27Pick up item = 27.
22L6start=3 item=27 pos=30 values after start are smaller than 27, so 27 belongs at index 3.
23L7start=3 item=27 pos=327 is already in place.
24L4start=4 item=38Pick up item = 38.
25L6start=4 item=38 pos=40 values after start are smaller than 38, so 38 belongs at index 4.
26L7start=4 item=38 pos=438 is already in place.
27L4start=5 item=43Pick up item = 43.
28L6start=5 item=43 pos=50 values after start are smaller than 43, so 43 belongs at index 5.
29L7start=5 item=43 pos=543 is already in place.
30L19Sorted with 47 comparisons, 7 writes. Writes never exceed n.

Practice ladder

Now try it yourself

Easy

  • Implement cocktail sort with shrinking start/end
  • Run gnome sort by hand on [3, 1, 2]

Medium

  • Shell sort with Knuth's gap sequence
  • Count writes made by cycle sort vs selection sort

Advanced

  • Find the minimum swaps to sort a permutation using cycle decomposition
  • Benchmark comb sort's shrink factor between 1.2 and 1.5

Key takeaways

  • Cocktail and comb sort fix bubble sort's slow turtles in different ways.
  • Gnome sort is insertion sort with swaps; shell sort is insertion sort with shrinking gaps.
  • Cycle sort minimises writes at the cost of O(n²) comparisons.