Merge Sort Lesson- Complete Computer Science Tutorial

What Merge Sort Actually Is

Merge Sort is a divide-and-conquer sorting algorithm. It splits an array in half repeatedly until you get single elements, then merges those elements back together in sorted order.

That's the whole thing. Split, sort, merge. No magic.

It was invented by John von Neumann in 1945. It's been around longer than most of the computers you're using right now.

Why You Should Care

Merge Sort guarantees O(n log n) time complexity in all cases. Worst, average, best—same performance.

Compare that to Quick Sort, which can degrade to O(n²) if you pick bad pivots. Merge Sort doesn't have that problem.

It's also stable. If two elements have equal values, their original order is preserved. This matters when you're sorting objects by multiple criteria.

How Merge Sort Works

The Divide Step

You take an array and split it in half. Then you split each half in half. Keep going until every element is isolated in its own array.

Example with [38, 27, 43, 3, 9, 82, 10]:

The Conquer (Merge) Step

Now you merge adjacent arrays, comparing elements and building sorted results.

Continue merging:

Final merge:

Sorted.

Time and Space Complexity

Case Time Complexity Space Complexity
Best O(n log n) O(n)
Average O(n log n) O(n)
Worst O(n log n) O(n)

The space complexity is the tradeoff. Merge Sort needs O(n) extra space for the temporary arrays during merging. Quick Sort can run in-place with O(log n) space.

When to Use Merge Sort

Use it when:

Skip it when:

Merge Sort vs Other Algorithms

Algorithm Best Time Avg Time Worst Time Space Stable
Merge Sort O(n log n) O(n log n) O(n log n) O(n) Yes
Quick Sort O(n log n) O(n log n) O(n²) O(log n) No
Heap Sort O(n log n) O(n log n) O(n log n) O(1) No
Insertion Sort O(n) O(n²) O(n²) O(1) Yes

Implementation in Python

Here's a working implementation. No frills, no class wrappers—just the algorithm.

def merge_sort(arr):
    if len(arr) <= 1:
        return arr
    
    mid = len(arr) // 2
    left = merge_sort(arr[:mid])
    right = merge_sort(arr[mid:])
    
    return merge(left, right)

def merge(left, right):
    result = []
    i = j = 0
    
    while i < len(left) and j < len(right):
        if left[i] <= right[j]:
            result.append(left[i])
            i += 1
        else:
            result.append(right[j])
            j += 1
    
    result.extend(left[i:])
    result.extend(right[j:])
    
    return result

Usage:

numbers = [64, 34, 25, 12, 22, 11, 90]
sorted_numbers = merge_sort(numbers)
print(sorted_numbers)  # [11, 12, 22, 25, 34, 64, 90]

Implementation in JavaScript

function mergeSort(arr) {
    if (arr.length <= 1) return arr;
    
    const mid = Math.floor(arr.length / 2);
    const left = mergeSort(arr.slice(0, mid));
    const right = mergeSort(arr.slice(mid));
    
    return merge(left, right);
}

function merge(left, right) {
    const result = [];
    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++]);
        }
    }
    
    return result.concat(left.slice(i)).concat(right.slice(j));
}

Common Mistakes

The Bottom Line

Merge Sort is reliable. It performs consistently, it's stable, and it's easy to understand. The trade-off is memory usage.

For most general-purpose sorting, languages use hybrid approaches—Introsort (Quick Sort + Heap Sort + Insertion Sort) or Timsort (Merge Sort + Insertion Sort). Python's `sorted()` uses Timsort. Java's `Arrays.sort()` uses Dual-Pivot Quick Sort for primitives and Timsort for objects.

But when you need guaranteed O(n log n) performance or stable sorting, Merge Sort is the straightforward choice. No gimmicks.