Timsort is the sort the real world actually runs: Python’s default, Java’s Arrays.sort for objects, and the JavaScript V8 engine’s array sort.
It is a hybrid — Insertion Sort for small chunks, Merge Sort for combining them — with one clever twist: it first looks for natural runs (already-sorted stretches) and exploits them.
Because real-world data is rarely random (slightly sorted lists, appended rows, merged logs), Timsort hits its near-linear best case far more often than any pure sort.
How It Works
- Detect runs: scan the array, finding maximal already-sorted (ascending) or strictly-descending segments.
- Normalize run size: if a run is shorter than a minimum size (e.g., 32–64, or 4 in the demo), extend it using Insertion Sort.
- Merge: combine adjacent runs with a balanced merge, using a stack to keep run lengths roughly powers of two.
- Repeat merging until one sorted run remains.
Key Insight
Two ideas make Timsort special: run detection and adaptive merging.
- Pure merge sort splits input blindly in half, paying
O(n log n)even on sorted data. - Timsort sees that big sorted stretches already exist and treats each as a completed run — sorting an already-sorted array is essentially one linear pass.
The run-stack also merges runs only when lengths are balanced, avoiding the pathological merges of a naive binary split.
Worked Example
Sort [5, 3, 8, 1, 2, 9, 7] with the demo’s run size of 4:
- Detect runs:
[5, 3]is descending → flip to ascending[3, 5];[8]follows → run[3, 5, 8](length 3);[1, 2, 9, 7]→ detect[1, 2, 9]ascending then7breaks it → run[1, 2, 7, 9]after insertion-sorting7. - Merge run 1 (
[3, 5, 8]) with run 2 ([1, 2, 7, 9]):- Compare heads:
1, 2, 3, 5, 7, 8, 9→[1, 2, 3, 5, 7, 8, 9]
- Compare heads:
Result: [1, 2, 3, 5, 7, 8, 9]. In the visualizer, watch runs get merged in a balanced tree — each merge is the two-pointer walk you know from Merge Sort.
Edge Cases & Pitfalls
- Already sorted input — one long run → nearly
O(n): Timsort’s best case. - Reverse sorted input — one long descending run, flipped in linear time, then a single merge pass: also near
O(n). - Random input — many tiny runs: the merge tree does the full
O(n log n), no worse than a good comparison sort. - Duplicates — Timsort is stable: runs preserve order and the merge keeps left-run elements first.
- Memory — requires
O(n)auxiliary space for merging, like merge sort.
Comparison With Other Sorts
| Scenario | Timsort | Quick Sort | Merge Sort |
|---|---|---|---|
| Real-world (semi-sorted) data | Near O(n) | O(n log n) | O(n log n) |
| Worst case | O(n log n) | O(n²) bad pivots | O(n log n) |
| Stability | Stable | Unstable | Stable |
| Adoption | Python, Java, V8, Rust | C, Go (Introsort) | External sort |
Applications
- Default object sort in Python, Java, Rust, and JavaScript engines
- Sorting data with structure — append-heavy logs, merged feeds, timestamped streams
- Any workload where “mostly sorted” inputs are plausible, because it detects and exploits them
Practice Trajectory
- Identify the natural runs in
[2, 5, 1, 4, 8, 7, 3]by hand. - Explain why a descending run can be converted to ascending in linear time.
- Trace the merge of runs
[1, 4, 7]and[2, 5, 8]with the two-pointer walk. - Argue why Timsort is near-linear on already-sorted data while pure merge sort is not.
- Implement the demo’s simplified version (run size 4 + insertion + merge) and compare it against plain merge sort on a sorted input.