DSA Trainer
← All patterns
Pattern

Sorting Algorithms Pattern

Put things in order so every other pattern becomes possible.

Quick Summary

Best used when

  • The problem becomes much simpler if the input is in order
  • You need O(n log n) and a comparison-based sort is the right tool
  • You need a stable sort to preserve relative order of equal elements
  • The problem explicitly requires you to implement a sorting algorithm

Clue words in the problem

sortorderascendingdescendingk largestrankarrangerearrange

Complexity gain

O(n²) naive ordering → O(n log n) divide-and-conquer sort

Why this pattern matters for LeetCode

Sorting is the unlock for dozens of other patterns. Two Pointers needs a sorted array. Binary Search needs a sorted array. Many greedy interval problems need intervals sorted by start or end time. Bubble sort and insertion sort are O(n²) and time out on large inputs. Merge sort and quicksort are O(n log n) — the fastest any comparison-based sort can be. Knowing which algorithm to use and why is as important as implementing one.

What the Sorting Algorithms pattern is

Sorting algorithms rearrange an array into a defined order. Comparison-based sorts are bounded by O(n log n) at best. Merge sort always achieves O(n log n) by splitting and merging. Quicksort achieves O(n log n) on average via partitioning but degrades to O(n²) in the worst case. Insertion sort is O(n²) in general but O(n) on nearly-sorted data. JavaScript's built-in Array.sort() uses Timsort (merge sort + insertion sort hybrid) and is O(n log n) stable.

Reach for Sorting Algorithms when you catch yourself thinking…

These are the internal questions that signal this pattern.

Would this problem be easier if the array were sorted?
Am I looking for the k-th largest/smallest, which requires ordering?
Does the pattern I want to apply (two pointers, binary search, greedy intervals) need sorted input?
Is the problem explicitly asking me to implement a sort?

Beginner mental model

Merge sort is like sorting a shuffled deck of cards with a friend. Split the deck in half, each of you sorts your half, then you merge by comparing your top cards and taking the smaller one. Quicksort is like picking a pivot card, putting all smaller cards to its left and all larger cards to its right, then recursing on each side. Insertion sort is like the way you'd sort a hand of playing cards: pick each new card and slide it leftward into its correct position.

Ready to practice Sorting Algorithms?

Guided hints, not answers. You build real intuition.

Start with Squares of a Sorted Array

When to use Sorting Algorithms

  • Merge sort: when O(n log n) guaranteed time is needed and O(n) auxiliary space is acceptable
  • Quicksort: when O(n log n) average time with O(log n) space is needed (in-place)
  • Insertion sort: when the input is nearly sorted (O(n) best case) or for small arrays
  • Array.sort(): for all other practical sorting in JavaScript interviews — it's O(n log n) stable
  • Counting/Bucket sort: when values are bounded integers in a small range (O(n + k))

When NOT to use Sorting Algorithms

  • The input is already sorted — sorting it again wastes time
  • You only need the maximum or minimum — a single O(n) linear scan is faster than sorting
  • You need the k-th element — a heap or quickselect is O(n) or O(n log k), faster than full sort
  • Values are unbounded strings and a custom comparator is complex — be careful about comparator correctness

Better alternative: If you only need one extreme, use a linear scan or heap. If you need the k-th element, use quickselect or a heap of size k.

JavaScript code template

Merge sort and quicksort implementations

// Merge sort — O(n log n) guaranteed, O(n) space, stable
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(l, r) {
  const out = [];
  let i = 0, j = 0;
  while (i < l.length && j < r.length) {
    out.push(l[i] <= r[j] ? l[i++] : r[j++]);
  }
  return out.concat(l.slice(i), r.slice(j));
}

// Quicksort — O(n log n) average, O(n²) worst, O(log n) space, unstable
function quickSort(arr, lo = 0, hi = arr.length - 1) {
  if (lo >= hi) return;
  const p = partition(arr, lo, hi);
  quickSort(arr, lo, p - 1);
  quickSort(arr, p + 1, hi);
}
function partition(arr, lo, hi) {
  const pivot = arr[hi];
  let i = lo;
  for (let j = lo; j < hi; j++) {
    if (arr[j] <= pivot) [arr[i++], arr[j]] = [arr[j], arr[i]];
  }
  [arr[i], arr[hi]] = [arr[hi], arr[i]];
  return i;
}

Merge sort splits at the midpoint, sorts each half recursively, and merges. Guaranteed O(n log n). Quicksort picks a pivot (last element here), partitions: elements ≤ pivot go left, elements > pivot go right. The pivot lands in its final sorted position. Recurse on each side. Average O(n log n) but degrades to O(n²) on already-sorted input with this pivot choice — use a random pivot in practice.

Step-by-step thinking process

Walk through this in your head before writing code.

  1. 1Ask: does the problem need a full sort, or just the max, min, or k-th element?
  2. 2If sorting: use Array.sort() in JavaScript for most interview problems (it's O(n log n) stable).
  3. 3If you must implement: choose merge sort for guaranteed O(n log n) and stability, quicksort for in-place O(log n) space.
  4. 4Write the comparator carefully for non-numeric data — a - b for ascending numbers, (a, b) => a.localeCompare(b) for strings.
  5. 5Remember sorting is often a preprocessing step that enables another pattern (two pointers, binary search, greedy).
B
Big_Wolverine_7575Founding Member· unprompted on Reddit

“This app finally made it click. I tried NeetCode, CTCI, and YouTube for years and still couldn't solve Two Sum. The beginner mental models for the patterns are genuinely so helpful. THANK YOU.

LeetCode-style problems that use this pattern

Practice on DSA Trainer with guided hints: no spoilers, just nudges.

Coming soon

  • Sort an Array

    Implement merge sort or quicksort from scratch — the canonical sorting algorithm problem.

    Coming soon
  • Merge Intervals

    Sort intervals by start time first — then a linear scan detects overlaps and merges them.

    Coming soon

Common mistakes beginners make

  • Using the default Array.sort() on numbers without a comparator — JavaScript sorts lexicographically by default, so [10, 2, 1].sort() gives [1, 10, 2]. Always pass (a, b) => a - b for ascending numeric sort.
  • Choosing bubble sort or selection sort (O(n²)) for large inputs — they time out. Use merge sort or quicksort for O(n log n).
  • Forgetting that quicksort can degrade to O(n²) on sorted input when always picking the last/first element as pivot — randomize the pivot or use the median-of-three heuristic.
  • Assuming sort is stable when it isn't — quicksort is not stable (equal elements can swap order). Use merge sort when relative order of equals matters.

Merge Sort vs Quicksort vs Insertion Sort

Choose the right tool for the job.

PatternUse when…
Merge SortGuaranteed O(n log n), stable sort needed, or sorting linked lists (O(1) merge with pointers)
QuicksortAverage O(n log n) with O(log n) space in-place, and worst-case O(n²) is acceptable with randomization
Insertion SortInput is nearly sorted (O(n) best case), or array is small (constant factors beat O(n log n) for n < ~16)

Time and space complexity

Time complexity

O(n log n) for merge sort / quicksort average; O(n²) for insertion/bubble sort

Space complexity

O(n) merge sort; O(log n) quicksort call stack; O(1) insertion/bubble sort

The O(n log n) lower bound for comparison-based sorting is proven: any comparison sort must make at least log₂(n!) comparisons, which by Stirling's approximation is Ω(n log n). Merge sort achieves this exactly. Quicksort achieves O(n log n) on average (random pivot); its worst case is O(n²) when the pivot is always the smallest or largest element. Insertion sort is O(n) when the input is already sorted (zero swaps needed).

Ready to practice Sorting Algorithms?

Guided hints, not spoilers. 150 problems across 33 patterns. Five are free, the rest unlock with a founding member seat.

Frequently asked questions

Why does JavaScript's Array.sort() sort numbers wrong without a comparator?
By default, Array.sort() converts elements to strings and sorts them lexicographically. '10' comes before '2' because '1' < '2'. For numeric sort always pass a comparator: arr.sort((a, b) => a - b) for ascending. This is one of the most common bugs in JavaScript interview code.
Is merge sort or quicksort better for interviews?
Merge sort is safer: guaranteed O(n log n), stable, and the implementation is clean. Quicksort is O(n log n) average but O(n²) worst case (though randomized pivot makes worst case astronomically unlikely). For interviews, implement merge sort if asked to implement a sort. If space is the concern, mention quicksort's O(log n) call stack vs merge sort's O(n) auxiliary arrays.
When should I reach for sort instead of a heap?
Sort when you need the full ordering of all elements. Heap when you only need the top K elements — a heap of size K gives O(n log k) which beats O(n log n) full sort when k << n. If you need to repeatedly query the maximum as elements are added and removed, a heap wins. If you sort once and query many times, sort wins.
What is a stable sort and when does it matter?
A stable sort preserves the relative order of elements with equal keys. If two elements have the same value, their original order is unchanged after the sort. Merge sort is stable; quicksort is not. Stability matters when you sort by one key and then by another — the second sort should preserve the order from the first. JavaScript's Array.sort() is guaranteed stable as of ES2019.

Explore other patterns