Skip to main content

Selection Sort

Overviewโ€‹

Selection sort divides the array into a sorted prefix and an unsorted remainder. Each round it scans the remainder for the smallest element and swaps it into place at the boundary.

It has one genuinely distinguishing property: it performs exactly n โˆ’ 1 swaps, the minimum possible for a sort that moves elements individually. Everything else about it is unremarkable.

Animation of selection sort: a marker scans the unsorted portion to find the smallest bar, which is then swapped into the boundary position, growing the sorted region one element at a time
Each round scans the whole remaining region for the minimum, then performs a single swap. The sorted prefix grows by exactly one element per round. Wikimedia Commons, CC BY-SA 3.0

Core Conceptsโ€‹

PropertyValue
Best caseO(nยฒ) โ€” no early exit is possible
AverageO(nยฒ)
Worst caseO(nยฒ)
SpaceO(1)
StableNo (in the standard swap-based form)
AdaptiveNo โ€” sorted input costs exactly as much as random input
SwapsO(n) โ€” exactly n โˆ’ 1

Architecture / Mechanismโ€‹

def selection_sort(a):
n = len(a)
for i in range(n - 1):
smallest = i
for j in range(i + 1, n): # scan the unsorted remainder
if a[j] < a[smallest]:
smallest = j
if smallest != i:
a[i], a[smallest] = a[smallest], a[i] # one swap per round
return a

The comparison count is fixed at n(nโˆ’1)/2 regardless of input โ€” the inner loop always runs to the end, because you cannot know an element is the minimum until you have seen every candidate. This is why there is no best case and no adaptivity.

Tracing [5, 1, 4, 2]:

RoundMinimum foundSwapResult
11 at index 15 โ†” 1[1, 5, 4, 2]
22 at index 35 โ†” 2[1, 2, 4, 5]
34 at index 2none[1, 2, 4, 5]

Why it is not stableโ€‹

Swapping a distant minimum into position jumps it over intervening elements, which can reorder equal values. With [2a, 2b, 1], the first round swaps 1 with 2a, giving [1, 2b, 2a] โ€” the two 2s have exchanged their original order.

Stability is recoverable by shifting the intervening block instead of swapping, but that costs O(n) writes per round and forfeits the algorithm's only advantage.

Practical Usageโ€‹

The reason to choose selection sort is when writes are far more expensive than reads:

  • EEPROM and flash memory have limited erase/write endurance and slow writes, while reads are cheap. See SSDs & NAND Flash โ€” minimising writes is the whole design pressure there.
  • Very large records with small keys, where each move copies a lot of bytes. Though in that case the better answer is usually to sort an array of indices or pointers instead, and permute once.

That is a narrow niche, and it is the entire case for this algorithm.

Edge Cases & Pitfallsโ€‹

  • No early exit exists. Adding a "did anything change?" check does nothing, because the inner scan is unconditional. Sorted input costs full price.
  • Assuming it is stable because it looks like it should be. It is not, and the failure appears only when sorting by a secondary key.
  • Selection sort and heapsort are the same idea. Both repeatedly extract the extreme from the unsorted region; heapsort just uses a heap to find it in O(log n) instead of O(n), which is exactly what converts O(nยฒ) into O(n log n).

Comparisonsโ€‹

SelectionBubbleInsertion
Comparisonsn(nโˆ’1)/2 alwaysO(nยฒ), O(n) if sortedO(nยฒ), O(n) if nearly sorted
Writesn โˆ’ 1 swapsO(nยฒ)O(nยฒ)
Best caseO(nยฒ)O(n)O(n)
StableNoYesYes
Choose whenWrites dominate costNeverSmall or nearly-sorted input

Referencesโ€‹

  • Cormen, Leiserson, Rivest & Stein, Introduction to Algorithms โ€” selection sort appears as Exercise 2.2-2, including the question of why the loop runs to n โˆ’ 1 rather than n.
  • Sedgewick & Wayne, Algorithms, 4th ed., ยง2.1 โ€” elementary sorts, with the write-count comparison made explicitly.

Books & Videosโ€‹

  • Heapsort โ€” selection sort with a heap doing the selection.
  • Insertion Sort โ€” the elementary sort to reach for by default.