cv.cm

Coding · Algorithms

Selection sort explained: how it works, code and complexity

5 min read

Short answer

Selection sort divides the list into a sorted part on the left and an unsorted part on the right. On each step it finds the smallest element in the unsorted part and swaps it into the first unsorted position. It always makes about n²/2 comparisons, so it is O(n²) in every case, but it performs at most n − 1 swaps and uses O(1) extra space. The usual array version is not stable.

Open Sort lines

The idea in plain words

Scan the whole list and find the smallest element. Swap it with the element at index 0; now index 0 is final. Scan the rest of the list from index 1, find the smallest, and swap it into index 1. Continue until only one element is left, which must be the largest.

Unlike bubble sort and insertion sort, selection sort does not benefit from input that is already sorted: it still has to scan every remaining element to be sure it found the minimum.

Worked example, step by step

Sort [64, 25, 12, 22, 11]. The part before the bar is sorted and final.

StepUnsorted partMinimum foundSwapList after the step
164, 25, 12, 22, 1111 at index 4index 0 ↔ 4[11 | 25, 12, 22, 64]
225, 12, 22, 6412 at index 2index 1 ↔ 2[11, 12 | 25, 22, 64]
325, 22, 6422 at index 3index 2 ↔ 3[11, 12, 22 | 25, 64]
425, 6425 at index 3none needed[11, 12, 22, 25 | 64]

Four steps for five elements, with 4 + 3 + 2 + 1 = 10 comparisons and three real swaps.

Python implementation

def selection_sort(items):
    """Sort a list in place, ascending. Returns the same list."""
    n = len(items)
    for i in range(n - 1):
        smallest = i
        for j in range(i + 1, n):
            if items[j] < items[smallest]:
                smallest = j
        if smallest != i:
            items[i], items[smallest] = items[smallest], items[i]
    return items
print(selection_sort([64, 25, 12, 22, 11]))   # [11, 12, 22, 25, 64]
print(selection_sort([2, 1]))                 # [1, 2]

The outer loop runs n − 1 times because the last remaining element is automatically in place. The inner loop only records an index; the swap happens once per outer step.

Time and space complexity

MeasureValueReason
Comparisons, every casen(n − 1)/2each step scans the whole unsorted part
Time, best / average / worstO(n²)the scan does not depend on the order
Swaps, worstn − 1at most one swap per step
Extra spaceO(1)in place, one index variable

The small number of writes is the one real strength of selection sort. If writing to memory is much more expensive than reading, as with some flash memory or when moving large records, fewer writes can matter. Cycle sort takes this further and writes each element at most once.

Stability: why selection sort is not stable

A stable sort keeps equal elements in their original relative order. The long-distance swap in selection sort can jump an element over another one that is equal to it.

def selection_sort_by_key(items, key):
    n = len(items)
    for i in range(n - 1):
        m = min(range(i, n), key=lambda j: key(items[j]))
        items[i], items[m] = items[m], items[i]
    return items
cards = [("4", "hearts"), ("4", "spades"), ("2", "clubs")]
print(selection_sort_by_key(cards, key=lambda c: c[0]))
# [('2', 'clubs'), ('4', 'spades'), ('4', 'hearts')]  order of the 4s changed

The 2 was swapped with the 4 of hearts, which moved the 4 of hearts behind the 4 of spades. A stable version exists: instead of swapping, remove the minimum and insert it at position i, shifting the others right. This keeps the order but costs O(n) writes per step.

How selection sort compares with other sorts

AlgorithmBestAverageWorstExtra spaceStable
Selection sortO(n²)O(n²)O(n²)O(1)No
Bubble sortO(n)O(n²)O(n²)O(1)Yes
Insertion sortO(n)O(n²)O(n²)O(1)Yes
HeapsortO(n log n)O(n log n)O(n log n)O(1)No
Merge sortO(n log n)O(n log n)O(n log n)O(n)Yes
QuicksortO(n log n)O(n log n)O(n²)O(log n)No

Heapsort is selection sort with a better data structure: it also repeatedly selects the largest remaining element, but finds it in O(log n) using a binary heap instead of an O(n) scan, which brings the total to O(n log n).

When to use selection sort

Use the built-in sorted() or list.sort() in real Python code. Selection sort is worth knowing as a teaching example, for very small fixed-size arrays in constrained environments, and when the number of writes must be minimal. It is a frequent exam and interview question, usually as: trace the steps, count the comparisons and swaps, and explain why it is not stable.

FAQ

Is selection sort stable?

The standard in-place array version is not stable, because a long-distance swap can move an element past an equal one. A version that inserts instead of swapping is stable but does more writes.

What is the time complexity of selection sort?

O(n²) in the best, average and worst case. It always makes n(n − 1)/2 comparisons, regardless of the input order.

How many swaps does selection sort make?

At most n − 1, one per outer step. This is fewer than bubble sort or insertion sort, which can make O(n²) swaps or shifts.

What is the difference between selection sort and insertion sort?

Selection sort picks the minimum of the unsorted part and appends it to the sorted part. Insertion sort takes the next element and inserts it into the right place in the sorted part. Insertion sort is stable and has an O(n) best case; selection sort is neither.

All coding tutorials