Module P-9·12 min read

Merge sort, divide-and-conquer, and the stability guarantee that makes it the default for correctness-critical sorts.

Written by Jatin Jain Saraf · Architect of SupraScan

Module P-9 — Sorting II: Merge Sort and Divide-and-Conquer

What this module covers: Merge sort is the first algorithm in this course to fully embody divide-and-conquer: split the problem in half, solve each half recursively (Module P-1), then combine the results. This module covers how that splitting produces O(n log n) instead of Module P-8's O(n²), why merge sort is stable (a property that matters more than it sounds like it should), and its O(n) space cost — the trade-off Module P-10's quicksort makes differently.


Divide-and-Conquer, Concretely

The strategy: split the array in half, recursively sort each half, then merge the two already-sorted halves back together in one linear pass — the exact merge logic already built for linked lists in Module P-2, applied here to arrays instead.

function mergeSort(arr: number[]): number[] {
  if (arr.length <= 1) return arr; // base case (Module P-1)
 
  const mid = Math.floor(arr.length / 2);
  const left = mergeSort(arr.slice(0, mid));  // recursively sort the left half
  const right = mergeSort(arr.slice(mid));    // recursively sort the right half
 
  return merge(left, right); // combine two sorted halves into one sorted array
}
 
function merge(left: number[], right: number[]): number[] {
  const result: number[] = [];
  let i = 0, j = 0;
 
  while (i < left.length && j < right.length) {
    if (left[i] <= right[j]) result.push(left[i++]);
    else result.push(right[j++]);
  }
 
  // One side will have leftover elements — they're already sorted, append them directly
  while (i < left.length) result.push(left[i++]);
  while (j < right.length) result.push(right[j++]);
 
  return result;
}

merge is the exact two-pointer shape from Module F-8 and Module P-2's mergeSorted: since both left and right are already sorted, comparing their current fronts and taking the smaller one — repeated until one side runs out — produces a fully sorted result in a single O(n) pass, no re-comparison of anything already placed.


Why Splitting Gets You O(n log n)

mergeSort splits the array in half, recursively, until reaching single-element arrays (already trivially sorted) — then merges pairs back together on the way back up. The recursion depth is exactly Module F-11's binary-search-style halving: splitting n elements in half repeatedly takes log₂(n) levels to reach single elements.

Level 0 (splitting):        [8 elements]
Level 1:              [4 elements]  [4 elements]
Level 2:          [2][2]        [2][2]
Level 3:        [1][1][1][1]  [1][1][1][1]     <- log₂(8) = 3 levels of splitting

At each level of this structure, merging back together processes every element exactly once — the total work merging at any single level is O(n), no matter how many pieces that level is split into (four merges of 2 elements each is the same total work as one merge of 8, or two merges of 4). With log(n) levels, each doing O(n) total work, the overall cost is O(n) × O(log n) = O(n log n) — the same complexity class as Module P-10's quicksort (on average), and a full complexity class better than every sort in Module P-8.

At n = 10,000, that's the difference between roughly 10,000 × log₂(10,000) ≈ 133,000 operations (merge sort) and 10,000² = 100,000,000 operations (bubble/selection/insertion sort) — a concrete, enormous gap that only widens as n grows further.


Stability: Why It's Worth Naming

A sort is stable if it preserves the relative order of elements that compare as equal. Merge sort is stable, specifically because of one small detail in merge: left[i] <= right[j] (using <=, not <) always prefers taking from the left side on a tie — and since left came before right in the original array, this guarantees that two equal elements never get reordered relative to each other.

const people = [
  { name: 'Bob', age: 30 },
  { name: 'Amy', age: 25 },
  { name: 'Cal', age: 30 },
];
 
// Sort by age. Stability guarantees Bob still comes before Cal afterward,
// since they were already in that relative order before sorting by age.

Sign in to keep reading

The rest of this module is free — sign in with Google to unlock it and track your progress.

Sign in & Register

Discussion

0

Join the discussion

Loading comments...