Module P-10·16 min read

Quick sort, partitioning, and pivot selection — the in-place O(n log n) sort most languages actually ship.

JJS
Written by Jatin Jain Saraf · Senior Software Engineer

Module P-10 — Sorting III: Quick Sort and Partitioning

What this module covers: Quick sort is divide-and-conquer with a different split than Module P-9's merge sort: instead of splitting the array in half blindly and merging afterward, it partitions the array around a chosen value so that combining the two sides afterward requires no work at all. This module covers partitioning (the same three-pointer shape as Module F-8's Sort Colors), pivot selection, and the honest trade-off — average-case O(n log n) that can degrade to O(n²) on the wrong input.


Partitioning: Do the Combining Work First

Quick sort picks a pivot value, then rearranges the array so that everything smaller than the pivot ends up to its left, and everything larger ends up to its right — with the pivot itself landing in its final, correct sorted position. Once that's done, the two sides can be sorted independently and recursively, with no merge step needed afterward, because the partition already guaranteed every element on the left belongs before every element on the right.

typescript

This is the identical shape as Module F-8's sortColors (Dutch national flag): a boundary pointer (i) tracks "confirmed smaller," a scanning pointer (j) walks forward once, and elements are swapped into place as the scan proceeds — one pass, O(n), no auxiliary array. The pivot ends up exactly at index i + 1, with every smaller element to its left and every larger element to its right, guaranteed.


Quick Sort: Partition, Then Recurse on Each Side

typescript

Compare this directly to Module P-9's mergeSort: merge sort splits blindly (down the middle) and does real, O(n) work after the recursive calls return (the merge step); quick sort does its O(n) work before recursing (the partition), and needs nothing at all afterward — the two sorted halves, once partitioned correctly, are already a fully sorted whole array. This is why quick sort sorts in place: no second array is ever allocated to hold merged results — every operation happens by swapping elements within the original array.


Why Pivot Choice Determines Everything

If partitioning always split the array into two genuinely equal halves, quick sort would have the exact same O(n log n) shape as merge sort — log(n) levels of recursion, O(n) partitioning work at each level. But partitioning only produces two balanced halves if the pivot happens to land near the median of the current subarray. Choosing the pivot poorly can produce wildly unbalanced splits:

typescript

Every partition peels off exactly one element instead of roughly halving the remaining problem — this degenerates into O(n) levels of recursion instead of O(log n), each still doing O(n) partitioning work, for an overall O(n²) worst case. This is structurally the identical failure mode as Module P-5's BST degenerating on sorted input: a strategy that's efficient on average can become linear-chain-like on specifically adversarial input, and "already sorted" turns out to be exactly that adversarial case for a naive last-element pivot choice.

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...

© 2026 Jatin Jain Saraf (JJS). All rights reserved.