Algorithms
Timsort: Run Detection and Galloping Merge
Sort a list that is already 30% ordered and Timsort finishes in a fraction of the comparisons a textbook merge sort would spend — on fully sorted input it makes a single O(n) pass and stops. That is not luck: Timsort actively hunts for pre-existing order. It is a stable, adaptive, natural mergesort — a hybrid of merge sort and binary insertion sort — designed by Tim Peters in 2002 for the CPython interpreter, and it is the default sort in Python, Java (for objects), Android, Rust's slice::sort, V8, and Swift.
Its two signature tricks are run detection (scan the input into monotonic "runs," extending short ones with binary insertion sort) and the galloping merge (when one run keeps "winning," switch from one-at-a-time comparison to exponential search to skip ahead). Together they give O(n) best case, O(n log n) worst case, and real-world speedups on the partially-ordered data that dominates practice.
- TypeHybrid stable adaptive mergesort
- Time (best / avg / worst)O(n) / O(n log n) / O(n log n)
- SpaceO(n) worst case (temp buffer ≤ n/2)
- InventedTim Peters, 2002 (CPython)
- Used inPython, Java, Android, Rust, V8, Swift
- Key ideaDetect natural runs; merge with galloping
Interactive visualization
Press play, or step through manually. The visualization is yours to drive — try it before reading on.
Watch the 60-second explainer
A condensed visual walkthrough — narrated, captioned, under a minute.
The problem: real data is not random
Classic complexity analysis assumes worst-case, adversarial input. Real-world arrays are almost never random: log timestamps arrive nearly sorted, a database index gets a few rows appended, a merged dataset is a concatenation of already-sorted chunks. A plain merge sort ignores this and always does its full ~n·log₂n comparisons; quicksort can even degrade to O(n²) on such structured input.
Timsort's premise is that partially-ordered input should cost less than random input. An algorithm with this property is called adaptive, and the standard formal target is to run in O(n + n·log(n/r)) time, where r is the number of natural runs — dropping to O(n) when the data is one long run. Tim Peters designed it for CPython's list.sort(), where two extra requirements were non-negotiable:
- Stability — equal elements must keep their original order (Python sorts tuples, dicts, records by key).
- Few comparisons — in Python each comparison is an expensive dynamic dispatch, so minimizing comparison count matters as much as minimizing swaps.
How it works, step by step
Timsort processes the array left to right using an explicit stack of pending runs.
- Compute minrun. Pick
minrunin [32, 64] from the array lengthnso thatn/minrunis at or just below a power of two — this keeps the eventual merges balanced. (It takes the top 6 bits ofnand adds 1 if any lower bit is set.) - Detect a run. Starting at the current index, scan the longest ascending (a ≤ b ≤ c) or strictly descending (a > b > c) contiguous run. A descending run is reversed in place — strict descent is required so the reversal stays stable.
- Extend to minrun. If the natural run is shorter than
minrun, grab the next elements and use binary insertion sort to extend it tominrunlength. - Push and maybe merge. Push the run onto the stack, then enforce the merge invariants below by merging adjacent runs. When Timsort merges, it uses galloping mode to skip runs of consecutive winners.
The stack invariants (for run lengths A, B, C from the top) are: A > B + C and B > C. Whenever a new push violates them, Timsort merges to restore balance, which bounds the stack to O(log n) entries.
Complexity and a worked trace
Bounds: best case O(n) (input is already sorted — one run, no merges); average and worst case O(n log n); auxiliary space O(n), though the temp buffer needed for any single merge is at most the size of the smaller run, so effectively ≤ n/2. It is stable and does O(n) comparisons on already-sorted data.
Galloping: during a merge of runs X and Y, if one run wins MIN_GALLOP = 7 comparisons in a row, Timsort switches to galloping: it uses exponential + binary search to find how many elements of the winning run precede the other run's next element, copying that whole block at once. A block of k winners then costs O(log k) comparisons instead of k. If galloping stops paying off, MIN_GALLOP is nudged up and normal merging resumes.
input: [5,4,3, 7,8,9, 1,2,6] minrun ~ 3
run 1: 5 4 3 (descending) -> reverse -> 3 4 5
run 2: 7 8 9 (ascending) 7 8 9
run 3: 1 2 6 (ascending) 1 2 6
merge 3&2 -> 1 2 6 7 8 9
merge with 1 -> 1 2 3 4 5 6 7 8 9
Where it is used in real systems
Timsort is arguably the most-deployed sorting algorithm on Earth, because it sits under several language runtimes:
- Python —
list.sort()andsorted()since 2002 (CPython 2.3); the canonical implementation. - Java —
Arrays.sort()for object arrays andCollections.sort()since Java 7 (a port of Peters' C code). Primitive arrays use a dual-pivot quicksort instead because stability is irrelevant for primitives. - Android — the Java runtime's default object sort, so effectively every Android app.
- Rust —
slice::sort()(the stable sort) is a Timsort-derived driftsort/adaptive mergesort. - JavaScript — V8 (Chrome, Node.js) made
Array.prototype.sorta Timsort in 2018 to guarantee stability required by the ES2019 spec. - Swift — the standard library's
sort()uses a Timsort-style stable adaptive merge.
The common thread: these are general-purpose library sorts where stability and good behavior on structured input matter more than raw cache-friendliness on random arrays.
Compared to the alternatives
The right way to place Timsort is by what it trades away and what it buys.
- vs. plain merge sort: same O(n log n) worst case and same stability, but Timsort is adaptive — it exploits existing runs and gallops through skewed merges, so on partially-ordered data it does far fewer comparisons and drops to O(n) on sorted input. The cost is a considerably more intricate implementation.
- vs. quicksort / introsort: quicksort is usually faster on random data and sorts nearly in place (O(log n) stack), but it is not stable and its naive form is O(n²) on adversarial input. Timsort trades some raw speed and O(n) memory for stability, adaptivity, and a guaranteed O(n log n) ceiling.
- vs. heapsort: heapsort is O(1) space and O(n log n) worst case but is not stable, not adaptive, and cache-unfriendly.
The core tradeoff: Timsort spends O(n) auxiliary memory and code complexity to guarantee stability plus best-in-class performance on the ordered-ish data that real programs actually feed a sort.
Pitfalls, a famous bug, and significance
The 2015 invariant bug. In 2015 a team using the Java verification tool KeY (de Gouw et al.) proved that the run-merging invariants, as implemented, did not actually guarantee the merge stack stayed small — a crafted adversarial input could overflow the fixed-size runLen array and throw ArrayIndexOutOfBoundsException. The bug had lain dormant in Python, Java, and Android for over a decade. The fix: either enlarge the stack bound or, more robustly, check two invariants deeper (compare A>B+C and B>C plus the run below). Python patched it; Java initially just enlarged the array.
Other pitfalls: the O(n) temp buffer makes it unsuitable for hard memory-constrained embedded contexts; on truly random data it can be modestly slower than an in-place quicksort; and getting the descending-run reversal right (strict >, not ≥) is essential to preserving stability.
Significance: Timsort mainstreamed the idea that a production sort should be measured on real, structured inputs — and it remains the default in most major languages more than two decades later.
| Algorithm | Best | Average | Worst | Space | Stable | Adaptive |
|---|---|---|---|---|---|---|
| Timsort | O(n) | O(n log n) | O(n log n) | O(n) | Yes | Yes |
| Merge sort | O(n log n) | O(n log n) | O(n log n) | O(n) | Yes | No |
| Quicksort (Hoare/introsort) | O(n log n) | O(n log n) | O(n²) / O(n log n) | O(log n) | No | No |
| Heapsort | O(n log n) | O(n log n) | O(n log n) | O(1) | No | No |
| Binary insertion sort | O(n) | O(n²) | O(n²) | O(1) | Yes | Yes |
Frequently asked questions
Who invented Timsort and when?
Tim Peters invented it in 2002 for CPython, where it debuted in Python 2.3. He built it on ideas from Peter McIlroy's 1993 paper on optimistic natural mergesort. It is named after him.
Why is Timsort's best case O(n) instead of O(n log n)?
On already-sorted input, run detection finds the entire array as a single ascending run in one linear scan. With only one run there is nothing to merge, so the algorithm terminates after that O(n) pass. Merge sort, by contrast, always recurses and merges regardless of input order.
What is minrun and why is it between 32 and 64?
minrun is the minimum run length; short natural runs are padded up to it with binary insertion sort. It is chosen so that n/minrun is at or just below a power of two, which keeps the final merges balanced. The 32–64 window balances insertion sort's efficiency on small arrays against having too many runs to merge.
What is galloping mode?
When merging two runs, if one run wins MIN_GALLOP (7) comparisons in a row, Timsort switches to exponential-plus-binary search to find how many of that run's elements come before the other run's next element, then bulk-copies that block. This turns a block of k consecutive winners from k comparisons into O(log k), which is a big win when the runs interleave in long stretches.
Is Timsort stable, and does that matter?
Yes, Timsort is stable: equal elements keep their original relative order. This matters for multi-key sorting (sort by name, then stably by date) and is why Java uses it for object arrays and the JavaScript ES2019 spec mandates a stable Array.sort, which pushed V8 to adopt Timsort.
What was the famous Timsort bug?
In 2015, researchers using the KeY formal-verification tool proved the merge-stack invariants did not actually bound the pending-run stack for all inputs, allowing an adversarial array to overflow the fixed-size runLen buffer and crash with ArrayIndexOutOfBoundsException. The bug existed for over a decade in Python, Java, and Android before it was patched.