Skip to content

Sorting Algorithms

Six ways to sort a list, and why your exam cares which one you pick. Watch comparisons and swaps in the lab, then see O(n²) and O(n log n) pull apart on bigger inputs.

Class 11 to 12intermediate45 min4 lessons1 interactive lab
By the end you will be able to
  • Count the comparisons and moves an algorithm makes, not just its Big-O
  • Trace bubble, selection and insertion sort frame by frame
  • Explain how divide and conquer reaches O(n log n)
  • Say what stability means and when it matters
  • Choose the right sort for a given constraint, or correctly choose the built-in

Cost at a glance

bubble
O(n²) average, O(n) best
selection
O(n²) always
insertion
O(n²) average, O(n) best
merge
O(n log n), O(n) space
quick
O(n log n) average, O(n²) worst
heap
O(n log n), O(1) space

Lesson 1 of 4

Two operations, counted

Every comparison sort is built from two primitives: compare two elements, and move an element. Big-O usually counts comparisons, but moves matter just as much in practice, because writing to memory is often the expensive half. This is why two algorithms with the same O(n²) label can behave very differently on real data.

Run the lab on the same six values with each algorithm and read the counters. Selection sort makes the most comparisons and the fewest moves. Bubble sort makes many of both. Insertion sort's counts collapse when the data is already close to sorted. None of that is visible in the complexity class alone.

Interactive lab

Sorting algorithms

Bubble, selection and insertion sort on the same data, with live comparison and move counters so the quadratic cost is measured rather than asserted.

Sorting algorithms

best O(n) · average O(n²) · swaps up to O(n²)
42
0
8
1
15
2
4
3
23
4
16
5
0comparisons0swaps / moves0 of 6 in final position
1 / 28

Start

Bubble sort compares neighbours and swaps them when they are out of order. Each pass pushes the largest remaining value to the end, which is where the name comes from.

Continue

Lesson 2 of 4

The three quadratic sorts

Bubble sort repeatedly compares neighbours and swaps them when they are out of order, so the largest value bubbles to the end on each pass. It is the easiest to explain and the worst to use, but it has one virtue: if a full pass makes no swaps the data is sorted and it can stop, giving it an O(n) best case.

Selection sort scans the unsorted region for the smallest value and swaps it into place. Its comparison count is fixed at n(n-1)/2 no matter what the input looks like, so it has no best case, but it never makes more than n-1 swaps. That makes it the choice in the rare situation where writes are far more expensive than reads.

Insertion sort takes each element and walks it left into an already-sorted prefix, stopping as soon as it meets something no greater than itself. On nearly sorted data that stop happens immediately, so it approaches O(n). It is also stable and works well on tiny arrays, which is why built-in language sorts often switch to it for short runs.

Continue

Lesson 3 of 4

Divide and conquer: O(n log n)

Merge sort splits the array in half, sorts each half the same way, and merges the two sorted halves in one linear pass. The splitting depth is log₂ n and each level does O(n) work merging, which multiplies out to O(n log n). It is stable and its worst case is the same as its best, but it needs O(n) extra space for the merge.

Quicksort also partitions, but around a chosen pivot: values below the pivot to the left, above to the right, then recurse into each side. Its average is O(n log n) with excellent constants and no extra array, which is why it is usually the fastest in practice. Its worst case is O(n²) when the pivot choice is consistently terrible, which is why real implementations randomise or median-select the pivot.

Heap sort builds a heap and repeatedly extracts the maximum. It gives a guaranteed O(n log n) with O(1) extra space, but its memory access pattern jumps around, so it usually loses to quicksort on real hardware despite the identical complexity class.

No comparison sort can beat O(n log n) in the general case: there are n! possible orderings and each comparison rules out at most half of them, so log₂(n!) comparisons are needed, which grows as n log n. Sorts that do better, such as counting or radix sort, buy that speed by not comparing at all and instead assuming something about the keys.

Continue

Lesson 4 of 4

What to actually do

Use the language's built-in sort. It is not laziness: `Array.prototype.sort` in V8 and Python's `sorted` both use Timsort, a hybrid that finds already-ordered runs, sorts short runs with insertion sort and merges the rest. It is stable, it is O(n log n) worst case, and it beats hand-written sorts on real data because real data is rarely random.

Two things you still have to get right yourself. First, the comparator: in JavaScript, `[10, 9, 1].sort()` gives `[1, 10, 9]` because the default converts values to strings, so numbers need `(a, b) => a - b`. Second, stability, which matters whenever you sort by one key after another. Sort by name and then by department with a stable sort, and rows within a department stay alphabetical; with an unstable sort that order is silently lost.

Continue

Worked examples

Read the code, then change it

Copy any example into the playground and break it on purpose. That is the fastest way to learn what each line is holding up.

Counting the workJavaScript
function bubbleSort(input) {
  const a = [...input];
  let comparisons = 0;
  let swaps = 0;
  for (let pass = 0; pass < a.length - 1; pass += 1) {
    let swapped = false;
    for (let i = 0; i < a.length - 1 - pass; i += 1) {
      comparisons += 1;
      if (a[i] > a[i + 1]) {
        [a[i], a[i + 1]] = [a[i + 1], a[i]];
        swaps += 1;
        swapped = true;
      }
    }
    if (!swapped) break; // already sorted: the O(n) best case
  }
  return { sorted: a, comparisons, swaps };
}

const random = [42, 8, 15, 4, 23, 16];
const sortedAlready = [4, 8, 15, 16, 23, 42];
const reversed = [42, 23, 16, 15, 8, 4];

for (const [name, data] of [['random', random], ['sorted', sortedAlready], ['reversed', reversed]]) {
  const { comparisons, swaps } = bubbleSort(data);
  console.log(name.padEnd(9), comparisons, 'comparisons', swaps, 'swaps');
}
The comparator trapJavaScript
const numbers = [10, 9, 1, 100, 25];

console.log([...numbers].sort());              // [1, 10, 100, 25, 9] is string order!
console.log([...numbers].sort((a, b) => a - b)); // [1, 9, 10, 25, 100]

// Stability: sort by score, then by name, and equal scores stay alphabetical.
const players = [
  { name: 'Asha', score: 8 },
  { name: 'Bilal', score: 9 },
  { name: 'Chen', score: 8 },
];
const byName = [...players].sort((a, b) => a.name.localeCompare(b.name));
const byScore = byName.sort((a, b) => b.score - a.score);
console.log(byScore.map((p) => p.name + ':' + p.score).join(', '));

Practice

Work these out yourself

No answer key here on purpose: these are the questions worth thinking through before you move on. Open one and work it out.

0/5 attempted

Assessment

Check your understanding

Answer each question, then read the explanation. That is where the learning is.

0/5
  1. Question 1: How many comparisons does selection sort make on an array of 6 elements that is already sorted?
    Question 1 / 5

    How many comparisons does selection sort make on an array of 6 elements that is already sorted?

    Select an option first
  2. Question 2: Which sort is the best fit for an array that is already nearly in order?
    Question 2 / 5

    Which sort is the best fit for an array that is already nearly in order?

    Select an option first
  3. Question 3: What is quicksort's worst-case time complexity?
    Question 3 / 5

    What is quicksort's worst-case time complexity?

    Select an option first
  4. Question 4: A sorting algorithm is described as stable. What does that guarantee?
    Question 4 / 5

    A sorting algorithm is described as stable. What does that guarantee?

    Select an option first
  5. Question 5: In JavaScript, `[10, 9, 1].sort()` returns `[1, 10, 9]`. Why?
    Question 5 / 5

    In JavaScript, `[10, 9, 1].sort()` returns `[1, 10, 9]`. Why?

    Select an option first
5 of 5 questions left.

Finished this topic?

0 of 4 lessons marked done. Marking the topic complete ticks the rest.