Algorithm Search⌘K

Selection sort in Python

Sort by repeatedly selecting the smallest remaining item and swapping it into place.

File
selection_sort.py, 22 lines
Level
I0 not checked yet
Toolchain
Python 3.14, pytest 9
Test framework
pytest
Run the tests
python3 -m pytest
selection_sort.pyRawGitHub
# SPDX-License-Identifier: MIT
# Part of https://algorithm.tamnd.com/selection-sort/. The authors are listed in prov/6ADSAW.jsonl.

from typing import Protocol, TypeVar


class Ordered(Protocol):
    def __lt__(self, other: "Ordered", /) -> bool: ...


T = TypeVar("T", bound=Ordered)


def selection_sort(a: list[T]) -> None:
    """Sort a in place in non-decreasing order. Requires a total order; O(n²) time, O(1) extra space."""
    n = len(a)
    for i in range(0, n - 1):  # @line L1
        min_index = i  # @line L2
        for j in range(i + 1, n):  # @line L3
            if a[j] < a[min_index]:  # @line L4
                min_index = j  # @line L5
        a[i], a[min_index] = a[min_index], a[i]  # @line L6
test_selection_sort.py 86 lines
# SPDX-License-Identifier: MIT
# Part of https://algorithm.tamnd.com/selection-sort/. The authors are listed in prov/6ADSAW.jsonl.

import random

from selection_sort import selection_sort


def test_example() -> None:
    a = [29, 10, 14, 37, 13, 5, 22]
    selection_sort(a)
    assert a == [5, 10, 13, 14, 22, 29, 37]


def test_edges_and_extremes() -> None:
    cases = (
        [],
        [7],
        [4, 4, 4, 4],
        [2, 1],
        [1, 2, 3, 4],
        [4, 3, 2, 1],
        [-(2**63), 2**63 - 1, 0, -1, 1],
    )
    for values in cases:
        a = list(values)
        selection_sort(a)
        assert a == sorted(values)


class Item:
    def __init__(self, key: int, label: str) -> None:
        self.key = key
        self.label = label

    def __lt__(self, other: "Item") -> bool:
        return self.key < other.key


def _select_last_minimum(a: list[Item]) -> None:
    """Sort a with <= at line L4, the wrong variant of the pitfall. Only for this test."""
    n = len(a)
    for i in range(0, n - 1):
        min_index = i
        for j in range(i + 1, n):
            if a[j].key <= a[min_index].key:
                min_index = j
        a[i], a[min_index] = a[min_index], a[i]


def test_equal_items_can_change_order() -> None:
    a = [Item(2, "a"), Item(2, "b"), Item(1, "c")]
    selection_sort(a)
    assert [(item.key, item.label) for item in a] == [
        (1, "c"),
        (2, "b"),
        (2, "a"),
    ]


def test_first_minimum_is_kept_and_last_minimum_reverses() -> None:
    first = [Item(2, "a"), Item(1, "b"), Item(1, "c")]
    selection_sort(first)
    assert [(item.key, item.label) for item in first] == [
        (1, "b"),
        (1, "c"),
        (2, "a"),
    ]

    last = [Item(2, "a"), Item(1, "b"), Item(1, "c")]
    _select_last_minimum(last)
    assert [(item.key, item.label) for item in last] == [
        (1, "c"),
        (1, "b"),
        (2, "a"),
    ]


def test_random_against_brute_force() -> None:
    rng = random.Random(2150)
    for _ in range(500):
        values = [rng.randint(-50, 50) for _ in range(rng.randint(0, 30))]
        expected = sorted(values)
        a = list(values)
        selection_sort(a)
        assert a == expected

What the next level needs

  • the tests did not run in this build

Read the full page about Selection sort