Sorting algorithm

On this page 7

A sorting algorithm is a procedure that rearranges the elements of a collection into a defined order, such as ascending numbers or alphabetical names, according to a comparison rule or a key. Among algorithms in programming, sorting is one of the most studied problems, because sorted data makes searching, merging, deduplication and reporting far cheaper.

No single sorting algorithm is best in every situation. Each one balances time complexity, extra memory, stability and behavior on real data differently, and production libraries usually combine several methods into one hybrid.

How sorting algorithms work

Most general-purpose sorting algorithms are comparison sorts: they learn about the data only by asking whether one element is smaller than another. Insertion sort grows a sorted prefix one element at a time, merge sort divides the input, sorts the halves and merges them, quicksort partitions around a pivot, and heapsort repeatedly extracts the maximum from a binary heap.

Comparison sorts share a hard theoretical limit. Since n elements can be arranged in n factorial possible orders and each comparison has only two outcomes, any comparison sort needs at least the base-2 logarithm of n factorial comparisons in the worst case, which grows as n log n. This Ω(n log n) lower bound means that no comparison sort can beat merge sort or heapsort asymptotically.

Non-comparison sorts escape that bound by exploiting the structure of the keys. Counting sort and radix sort read the digits or values directly and can run in linear time when keys are integers or fixed-length strings from a limited range.

Key properties of a sorting algorithm

Four properties decide whether a sorting algorithm fits a task. Big O complexity describes how running time grows with input size, but the other three often matter just as much in practice.

PropertyMeaningWhy it matters
Time complexityGrowth of work in best, average and worst casePredicts behavior on large inputs
Space complexityExtra memory beyond the inputLimits use on large or embedded data
StabilityEqual keys keep their original relative orderRequired for multi-key sorting
AdaptivityFaster on input that is already partly sortedReal data often contains sorted runs
In-place operationWorks within the input array plus small overheadAvoids allocating a second buffer
Properties used to evaluate sorting algorithms

Stability deserves special attention. Sorting a customer list by city with a stable algorithm, after it was already sorted by name, yields customers grouped by city and still alphabetical within each city. An unstable sort may scramble that secondary order.

Comparison of common sorting algorithms

The table below summarizes the classic sorting algorithms. Complexities refer to n elements; k is the range of key values and d the number of digits.

AlgorithmAverage timeWorst timeExtra spaceStable
Insertion sortO(n²)O(n²)O(1)Yes
Merge sortO(n log n)O(n log n)O(n)Yes
QuicksortO(n log n)O(n²)O(log n) stackNo
HeapsortO(n log n)O(n log n)O(1)No
Counting sortO(n + k)O(n + k)O(n + k)Yes
Radix sortO(d · (n + k))O(d · (n + k))O(n + k)Yes
Classic sorting algorithms at a glance

Asymptotic class is not the whole story. Quicksort is usually the fastest in-place sort on random data because its inner loop is simple and cache friendly, even though heapsort has a better worst case. Insertion sort beats them all on very small arrays, typically below about 16 to 32 elements, and runs in O(n) on input that is already sorted.

Hybrid algorithms in standard libraries

Standard libraries rarely use a textbook algorithm on its own. They combine methods to get good average speed, a guaranteed worst case and good behavior on partly sorted data.

Library functionAlgorithm familyStableDesign idea
Python `list.sort`Timsort-style adaptive merge sortYesDetects natural runs and merges them
Java `Arrays.sort` for objectsTimSortYesStability required for object ordering
Java `Arrays.sort` for primitivesDual-pivot quicksortNoStability is invisible for primitive values
C++ `std::sort`Introsort in common implementationsNoQuicksort that falls back to heapsort
C++ `std::stable_sort`Merge sortYesUses a buffer when memory allows
Sorting in widely used standard libraries

Introsort illustrates the hybrid idea well: it starts with quicksort, switches to heapsort if the recursion depth exceeds about 2 log₂ n, which caps the worst case at O(n log n), and finishes small partitions with insertion sort.

Choosing and using a sorting algorithm

The first rule is to use the language’s built-in sort unless measurements show a real need for something else. Library sorts are heavily tuned and tested, and a custom implementation is a common source of subtle bugs.

SituationRecommended choiceReason
General data in memoryBuilt-in library sortTuned hybrid with predictable performance
Records sorted by several keysStable sort, least significant key firstEarlier orderings survive later passes
Millions of small integer keysRadix or counting sortLinear time beats n log n
Data larger than memoryExternal merge sortSorts chunks on disk and merges them
Only the top k items neededPartial sort or heap selectionAvoids sorting the whole collection
Decision guide for sorting

Common mistakes include writing an inconsistent comparison function, for example one that is not transitive, which can produce wrong results or even exceptions in some libraries. Another is choosing a naive quicksort with the first element as pivot, which degrades to O(n²) on already sorted input.

Explained without jargon

A sorting algorithm is a recipe for putting things in order. Think of arranging a shuffled deck of cards from lowest to highest. One person picks up cards one at a time and slides each into place. Another splits the deck into small piles, sorts each pile and then combines them.

Both methods end with the same ordered deck. Yet they need very different amounts of effort as the deck grows. The key point is that the right method saves enormous time once there are thousands or millions of items.

A typical use case

An online store exports 1 million orders each night and needs them sorted by customer and then by date for a billing report. A developer first wrote a simple insertion sort, which on random data needs on the order of n² / 4, or about 250 billion comparisons, and the job did not finish within its time window.

Switching to the language’s built-in stable sort reduced the work to roughly n log₂ n, or about 20 million comparisons. The developer sorted by date first and then by customer, relying on stability to keep each customer’s orders in date order. The nightly sort now completes in well under a second of processing time, and the report is ready before the start of the business day.