Insertion sort 2026-10-13
Insertion sort grows a sorted prefix of an array one entry at a time. In the adjacent-swap version, start with position , compare its current sort key with the preceding sort key, and swap the complete sorting records only when the right sort key is strictly smaller. Continue leftward until no swap is needed or the entry reaches position zero; then process original position . A tie stops the pass. This is the strict comparison and exchange rule in the authored implementation.[ref]
Take , where the subscript labels a complete sorting record and comparisons inspect only its sort key. Starting from index zero, the complete key-comparison trace is:
ComparisonRight sort key smaller than left?ActionArray after the action
1, insert position : yesSwap positions .
2, insert position : noStop this pass on a tie.
3, insert position : yesSwap positions .
4, continue leftward: yesSwap positions .
5, continue leftward: noStop this pass on a tie.
Figure 1.
Insertion sort: five sort key comparisons and three complete-entry swaps
. Each row after the input shows the state after one comparison. Orange cells mark an adjacent swap; green cells mark a stopping tie. Identity letters are not compared. Both equal-key groups retain their input order, demonstrating stable sorting. Details.
The loop invariant is that, before pass , positions satisfy sortedness and contain precisely the entries originally in that prefix. Moving the next entry left through strictly larger sort keys extends that prefix, and every swap retains sorting permutation preservation. Equal-key entries never cross, giving stable sorting. Each leftward step decreases the position, and each completed pass increases the prefix length, so the algorithm terminates.
With constant-cost key comparisons and swaps, reverse-ordered distinct sort keys cause comparisons and swaps. Thus worst-case time complexity is in Big O notation. An already sorted array needs only key comparisons for and no swaps. The adjacent-swap version uses constant auxiliary space complexity. Small or nearly sorted arrays can therefore make insertion sort useful despite its quadratic worst case.
Sort the two halves ascending, reverse the second, and concatenate to form a bitonic sequence:
sort :: Ord a => [a] -> [a]
sort []  = []
sort [x] = [x]
sort xs  = merge (sort as ++ reverse (sort bs))
  where (as,bs) = halve xs
For a power-of-two length greater than one, both halves are smaller powers of two. By induction, the recursive calls return ascending permutations of their respective inputs. Reversal makes the second half descending, so part (e)'s precondition holds and merge sorts their concatenation without losing entries. This proves both sortedness and sorting permutation preservation. Its recurrence relation is , so summing the levels gives time. Equal keys cause no difficulty; ascending and descending here permit equality. This is a bitonic sorting algorithm.
After passes, , the array is in nondecreasing order of , the integer represented by its lowest numerical digits. Equivalently it is ordered by the tuple under lexicographic order, with the most significant processed numerical digit first. Among equal suffixes it retains the original order of the complete sorting records.
The loop invariant has a simple mathematical induction proof. Before any pass, for every sort key, so the original array satisfies it. Suppose it holds after passes. The next stable digit sort puts smaller groups first and preserves the existing order within each tie. Since
the new array is ordered by . Equal new suffixes have equal current numerical digits and equal old suffixes, so their original order is also preserved. Every pass is a permutation of complete sorting records, establishing sorting permutation preservation. At termination , the assumption gives : the invariant becomes whole-key sortedness, together with stable sorting.
Sorting algorithm 2026-10-13
A sorting algorithm rearranges a finite sequence into nondecreasing order under a chosen total order on its keys. Its output must contain every input entry exactly once, including repetitions: producing ordered values is insufficient if entries are lost. Merge sort is one such algorithm; stable sorting adds a requirement about equal keys.
Sorting creates reusable structure: equal sort keys become adjacent, and a sorted array supports binary search. However, an algorithm correctness check must establish both sortedness and sorting permutation preservation. Replacing every entry by the smallest key can produce ordered keys while destroying the original data. Stable sorting imposes a third condition when equal sort keys occur: retain their arrival order.
For example, take complete sorting records , with the first component as the sort key and the letter as associated data. A correct output is . The sort keys are ordered, every complete sorting record remains, and the equal-key pairs keep before and before . The output satisfies sortedness and sorting permutation preservation, but fails stable sorting. The output has the right multiset of sort keys but loses and duplicates ; it fails sorting permutation preservation of complete sorting records.
To express stable sorting precisely, label every input occurrence by its original position, even when two complete sorting records have identical contents. Let be the output position of the occurrence originally at position . Besides sortedness and sorting permutation preservation, require
Here is the sort key function. Sorting by the pair under lexicographic order gives the same order as stable sorting by : the original position breaks ties without discarding associated data. For a two-field ordering, first apply stable sorting by the secondary sort key, then apply stable sorting by the primary sort key. The second pass retains the secondary order within each group of equal primary sort keys. For instance, first becomes by letter, then by number.
Sorting record 2026-10-14
A complete entry comprising a sort key together with any associated data. In , the sort key is and stands for the associated data or an identity label. Sorting permutation preservation concerns these complete entries, rather than only their extracted sort keys.
Stable digit sort 2026-10-18
A stable digit sort applies stable sorting to complete sorting records using one specified numerical digit of their encoded sort keys. Counting sort can supply it; stable record placement in counting sort moves records without discarding their associated data. Stability concerns equal current numerical digits, even when the full sort keys differ. This is the information-preservation step in least-significant-digit radix sort.[ref]
An original two-record counterexample exposes the need for the tie rule. Start with at width three. A units pass gives . Both tens numerical digits are four. A deliberately unstable tens pass may reverse their tie and return : it is sorted by the tens numerical digit, yet its two-position suffixes are decreasing. The final stable hundreds pass sees two zeros and keeps this wrong order. Sorting permutation preservation still holds, but sortedness fails. Stability in the tens pass would have retained the units information.
With counting sort as the stable digit sort, each pass initializes counters, counts current numerical digits, constructs their prefix sums, and places complete sorting records. Thus each pass costs , and passes cost in Big O notation. Reuse the counters and two arrays of record references between passes: auxiliary space complexity is , rather than . Assume constant-cost numerical digit extraction, counter arithmetic, array indexing and record-reference movement; the bound counts these operations, not the cost of copying arbitrarily large payloads or performing arbitrary-precision integer arithmetic.
The benefit is avoiding a counter for every possible whole sort key: width has possibilities, while each pass needs only counters. A larger radix reduces the number of passes for a fixed key range but enlarges the counter array. The comparison sorting lower bound does not apply to these direct array indices; they reveal more than a two-way comparison. Nor does fixing make disappear: growing key width still increases work.
Initialize a mutable endpoint array to the inclusive cumulative counts . Scan input array indices . For each complete sorting record with sort key , first decrement , then write at output array index . This is equivalent to writing at the old endpoint minus one and then decrementing the endpoint.[ref]
In the worked example, the first input sorting record read is at array index six. Its endpoint changes from to , so the complete sorting record goes to . Next, changes from seven to six and goes to . Writing before the decrement at the old endpoint would put outside its reserved integer interval and try to put beyond the output array. All seven writes are:
Each endpoint changes only when a sorting record with its own sort key is placed. Consequently, stays at five through the placements of before dropping to four for .
Figure 1.
A backward input scan gives forward order among equal sort keys
. Original complete trace of the seven-sorting record example. Each row shows the endpoint decrement and resulting output array; the newly written complete sorting record is dark. Both input and output array indices start at zero. The final order is . Details.
Here is a complete implementation under the stated input contract; each sorting record is a pair whose first component is its integer sort key:
def counting_sort_records(records, K):
    end = [0] * K
    output = [None] * len(records)
    for key, data in records:
        end[key] += 1
    for key in range(1, K):
        end[key] += end[key - 1]
    for i in range(len(records) - 1, -1, -1):
        key = records[i][0]
        end[key] -= 1
        output[end[key]] = records[i]
    return output
The same array end successively stores frequencies, inclusive cumulative counts, and moving exclusive endpoints; the formulas describe their mathematical values without requiring three stored arrays. The write copies the complete sorting record, including its associated data. In particular, two occurrences of an identical pair still cause two separate writes.
For a short algorithm correctness proof, let be the number of already placed sorting records with sort key . A loop invariant is
All integer intervals here mean output array indices. Initially , so each whole reserved integer interval is unfilled. When the next sorting record has sort key , fewer than such occurrences have been placed, giving . Decrementing selects a fresh slot inside its reserved integer interval. At termination, and : every output slot has been filled exactly once with a complete input occurrence. The disjoint, increasing sort key ranges establish sortedness, while the fresh-slot property establishes sorting permutation preservation.
For stable sorting, take two input occurrences with equal sort keys at array indices . The backward scan visits first and puts it farther right in their shared integer interval. When is visited, the endpoint has decreased at least once, so its output position is smaller. Thus their original order survives. A forward scan with this same decrement rule reverses every equal-sort key group: it would put at positions five, four, three. A forward scan can instead be stable if it starts from and fills each integer interval left to right, incrementing after each write. The scan direction and endpoint convention must agree.
Count initialization takes writes, output initialization takes writes, counting takes updates, the prefix sum pass takes updates, and placement takes writes. Hence time complexity and auxiliary storage are in Big O notation, assuming constant-cost sort key access, integer arithmetic, array indexing and complete sorting record movement. Large records can instead be represented by constant-size references.[ref]
For example, three sorting records with sort keys zero, and zero require one million counters if ; unused sort keys still incur initialization and prefix sum work. The bound is therefore linear in the input length only when . Empty input skips the record scans, but this implementation still initializes counters and accumulates their prefix sums. For nonempty input, counting all records and filling their ranges gives a concrete termination argument.
Direct sort key indexing is a different operation from a binary comparison. The comparison sorting lower bound concerns comparison sorts; counting sort uses its restricted integer sort keys as array indices. Its small- speed is particularly useful for categorical numerical labels and for each stable sorting pass of radix sort: the reserved integer intervals turn group sizes into exact destinations.