Sorting Algorithms

Fundamental algorithms for arranging elements in a specific order.

Why Sorting Matters

Sorting is one of the most fundamental operations in computer science. Many other algorithms (like binary search) require sorted data to work efficiently.

💡 Fun fact: It's estimated that 25% of all CPU cycles are spent on sorting!

Algorithm Comparison

AlgorithmBestAverageWorstSpaceStable
Bubble SortO(n)O(n²)O(n²)O(1)
Selection SortO(n²)O(n²)O(n²)O(1)
Insertion SortO(n)O(n²)O(n²)O(1)
Merge SortO(n log n)O(n log n)O(n log n)O(n)
Quick SortO(n log n)O(n log n)O(n²)O(log n)
Heap SortO(n log n)O(n log n)O(n log n)O(1)

🫧 Bubble Sort

Repeatedly swaps adjacent elements if they're in the wrong order. Like bubbles rising to the surface—largest elements “bubble up” to the end.

⚠️ Not recommended for large datasets. O(n²) is very slow!

function bubbleSort(arr) {
    const n = arr.length;
    for (let i = 0; i < n - 1; i++) {
        for (let j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                // Swap adjacent elements
                [arr[j], arr[j + 1]] = [arr[j + 1], arr[j]];
            }
        }
    }
    return arr;
}

// Example: [5, 2, 8, 1] → [1, 2, 5, 8]

🃏 Insertion Sort

Builds the sorted array one element at a time, like sorting playing cards in your hand. Great for small or nearly-sorted arrays!

Best for: Small arrays (n < 50) or nearly sorted data.

function insertionSort(arr) {
    for (let i = 1; i < arr.length; i++) {
        const key = arr[i];
        let j = i - 1;

        // Move elements greater than key one position ahead
        while (j >= 0 && arr[j] > key) {
            arr[j + 1] = arr[j];
            j--;
        }
        arr[j + 1] = key;
    }
    return arr;
}

🔀 Merge Sort

Divide and conquer: Split the array in half, sort each half, then merge them back together. Guaranteed O(n log n)!

💡 Merge Sort is stable (preserves order of equal elements) and has consistent O(n log n) performance. Used in many standard libraries.

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, ...left.slice(i), ...right.slice(j)];
}

⚡ Quick Sort

Pick a pivot, partition the array so all smaller elements are on the left and larger on the right, then recursively sort each partition.

⚠️ Worst case O(n²) with bad pivot choice (already sorted array). Use randomized pivot to avoid this!

function quickSort(arr, left = 0, right = arr.length - 1) {
    if (left < right) {
        const pivotIndex = partition(arr, left, right);
        quickSort(arr, left, pivotIndex - 1);
        quickSort(arr, pivotIndex + 1, right);
    }
    return arr;
}

function partition(arr, left, right) {
    const pivot = arr[right];  // Use last element as pivot
    let i = left - 1;

    for (let j = left; j < right; j++) {
        if (arr[j] < pivot) {
            i++;
            [arr[i], arr[j]] = [arr[j], arr[i]];
        }
    }

    [arr[i + 1], arr[right]] = [arr[right], arr[i + 1]];
    return i + 1;
}

Which Algorithm to Use?

📝
Small arrays (n < 50)

Use Insertion Sort — simple and fast for small data

🎯
Need guaranteed O(n log n)

Use Merge Sort — consistent performance, stable

General purpose, average case fast

Use Quick Sort — best average performance, in-place

💾
Limited memory

Use Heap Sort — O(1) space, guaranteed O(n log n)

🔤
Need stability

Use Merge Sort or Insertion Sort

Content adapted from Hello 算法 - Sorting by Krahets, licensed under CC BY-NC-SA 4.0.