Lesson 14 / 25
Choosing a Sorting Algorithm
A decision table based on data characteristics, the use of stability in multi-key sorting, hybrid implementations, and cases where sorting is unnecessary.
Contents
Seven algorithms and six distinct criteria have accumulated. This lesson gathers them into a single decision framework. The goal is not to memorize a list of sorts, but to see which property of the data at hand determines the choice.
A Combined View of the Criteria
| Algorithm | Worst | Average | Extra space | Stable | Adaptive |
|---|---|---|---|---|---|
| Insertion | Yes | Yes | |||
| Selection | No | No | |||
| Bubble | Yes | Yes | |||
| Merge | Yes | Depends on variant | |||
| Quick | No | No | |||
| Heap | No | No | |||
| Counting | Yes | No | |||
| Radix | Yes | No |
The table alone does not decide; the problem determines which column matters.
The Decision Table
| Property of the data | Suitable choice | Rationale |
|---|---|---|
| Few elements (tens) | Insertion | Small constant, simple code |
| Nearly sorted | Insertion or run-based merge | Low inversion count |
| Stability required | Merge, counting, radix | Stable by definition |
| Very tight memory | Heap | extra space, has a guarantee |
| Worst-case guarantee needed | Heap or merge | Does not fall into the quadratic case |
| General-purpose, on an array | Hybrid (quick + heap + insertion) | Practical speed and a guarantee |
| Linked list | Merge | Sequential access is enough |
| Data that does not fit in memory | External merge | Reads as a stream |
| Keys are small integers | Counting | , no comparison |
| Fixed-width key, many records | Radix | Linear per digit |
| Many repeated keys | Three-way quicksort | The equal region is never reprocessed |
| Only the largest elements | Heap of size | , no sorting needed |
Most of the rows are not mutually exclusive; if more than one condition holds, the more restrictive one decides. If “stability required” clashes with “tight memory”, a stable, in-place algorithm is sought — implementations that give both exist, but their constants are large.
Hybrid Implementations
General-purpose library sorts do not use a single algorithm. Two common designs exist, and both are assembled from this topic’s lessons.
Partition-based hybrid. It starts with quicksort. If recursion depth exceeds the threshold — meaning the splits keep being unbalanced — it switches to heap sort. Segments below a certain size are left to insertion sort. The result: quicksort’s practical speed, heap sort’s worst-case guarantee, insertion sort’s small constant.
Run-based hybrid. The runs already sorted in the input are found, short runs are extended with insertion sort, and the runs are then merged. It approaches linear on nearly sorted data, is stable, and stays in the worst case. In exchange, it requires extra space.
The shared lesson: real implementations do not claim any single algorithm is best; they use each algorithm in the region where it excels.
Multi-Key Sorting
Stability’s most common use is sorting by more than one criterion. There are two routes.
Composite key. The comparison function evaluates the criteria in order. It finishes in a single pass and does not require stability.
Successive stable sorts. Sorting is done first by the secondary criterion, then by the primary one. Because the second sort is stable, the secondary order of elements equal on the primary criterion is preserved.
records = [ ("Ankara", 3), ("Izmir", 1), ("Ankara", 1), ("Bursa", 2), ("Izmir", 3), ("Ankara", 2), ] # Secondary criterion (number) first, primary criterion (city) second step1 = sorted(records, key=lambda k: k[1]) step2 = sorted(step1, key=lambda k: k[0]) print(step2) # [('Ankara', 1), ('Ankara', 2), ('Ankara', 3), ('Bursa', 2), ('Izmir', 1), ('Izmir', 3)] # Single pass with a composite key — same result print(sorted(records, key=lambda k: (k[0], k[1])) == step2) # True
Both routes give the same result. The successive method is more flexible when the criteria are determined at run time (a table sorted by the column the user picks); the composite key is cheaper because it is a single pass.
Radix sort’s digit-by-digit operation follows the same principle: every digit is a criterion, and stability preserves the result of the previous criteria.
Comparison Cost
Asymptotic tables count comparison as constant. If the key is complex, this assumption breaks: comparing long strings is proportional to string length, and locale-sensitive sorting rules are far more expensive.
Two consequences follow. First, if comparison is expensive, the algorithm that reduces the number of comparisons (merge) is preferred; if moving is expensive, the one that reduces moves (selection). Second, an expensive key transformation can be done once, before sorting, and cached — not recomputed on every comparison throughout the sort. This is a direct application of the precomputation pattern from the Time and Space Trade-off lesson.
Cases Where Sorting Is Unnecessary
Sorting is an operation that does more than what is needed, and it is often called unnecessarily.
| Need | Instead of sorting | Cost |
|---|---|---|
| The largest or smallest element | A single scan | |
| The kth smallest element | Quickselect | Expected |
| The largest elements | A heap of size | |
| Whether a value exists | A hash table | Expected |
| Filtering out duplicates | A hash set | Expected |
| The median value | A selection algorithm | Expected |
If sorting itself is genuinely needed — presenting output in order, preparing for binary search, many-query use — its cost is warranted. Sorting to answer a single question means paying to do work that is .
The Point Where Measurement Decides
Asymptotic analysis determines the class; choosing within a class requires measurement. If two algorithms are both , the difference lies in the constant, and the constant depends on memory access pattern, key type, data size, and implementation.
Three rules apply when measuring: it should be measured with real data (random data does not carry the partial ordering of real data), it should be measured with the input size varied (a single size does not reveal the class), and how the measured durations scale with the input should be examined — the scale-growth table from the Reading Complexity Classes lesson is for exactly this job.
Summary
- The choice comes from the data’s properties, not from the algorithm itself: size, partial ordering, the proportion of repeated keys, key structure, memory constraint, and the stability requirement.
- General-purpose implementations are hybrids; they use each algorithm in the region where it excels.
- Multi-key sorting is done with successive stable sorts or with a composite key.
- If comparison is expensive, the algorithm that reduces comparisons is chosen; if moving is expensive, the one that reduces moves.
- Sorting is unnecessary for minimum, kth, first-, or existence questions; linear or expected-constant-cost solutions exist.
- The decision within a class is given by measurement; measurement is done with real data and at more than one input size.
Next Step
Searching and sorting have worked on one-dimensional data: elements had only an ordering relation between them. The next topic moves to data where elements have connections. The Data Structures course defined graphs and built breadth-first and depth-first search; now the problems of shortest path, minimum spanning tree, and maximum flow in weighted graphs will be covered.
To keep your progress and take notes, Log in
My notes
Log in to take notes.