Selection Sort: Algorithm and Implementation

Selection sort is one of the simplest comparison-based sorting algorithms you'll encounter. It belongs to the family of elementary sorting algorithms alongside bubble sort and insertion...

Key Insights

  • Selection sort guarantees exactly n-1 swaps regardless of input order, making it ideal when write operations are significantly more expensive than comparisons
  • The algorithm’s O(n²) time complexity makes it impractical for datasets larger than a few hundred elements, but its O(1) space complexity and simplicity make it useful in memory-constrained environments
  • Unlike bubble sort and insertion sort, selection sort performs the same number of comparisons regardless of whether the input is already sorted, partially sorted, or completely reversed

Introduction to Selection Sort

Selection sort is one of the simplest comparison-based sorting algorithms you’ll encounter. It belongs to the family of elementary sorting algorithms alongside bubble sort and insertion sort—algorithms that are easy to understand and implement but don’t scale well to large datasets.

You’ll most likely encounter selection sort in educational contexts, coding interviews, or legacy systems. While it’s rarely the right choice for production code handling significant data volumes, understanding selection sort provides foundational knowledge for grasping more sophisticated algorithms. It also has legitimate use cases that we’ll explore later.

The core idea is intuitive: repeatedly find the smallest element from the unsorted portion and move it to the end of the sorted portion. This mental model maps directly to how you might sort playing cards in your hand.

How Selection Sort Works

The algorithm divides the array into two logical sections: a sorted portion on the left and an unsorted portion on the right. Initially, the sorted portion is empty.

Here’s the process:

  1. Find the minimum element in the unsorted portion
  2. Swap it with the first element of the unsorted portion
  3. Expand the sorted portion by one element
  4. Repeat until the entire array is sorted

Let’s walk through sorting the array [64, 25, 12, 22, 11]:

Pass 1: Find minimum in [64, 25, 12, 22, 11] → 11 at index 4 Swap with index 0: [11, 25, 12, 22, 64]

Pass 2: Find minimum in [25, 12, 22, 64] → 12 at index 2 Swap with index 1: [11, 12, 25, 22, 64]

Pass 3: Find minimum in [25, 22, 64] → 22 at index 3 Swap with index 2: [11, 12, 22, 25, 64]

Pass 4: Find minimum in [25, 64] → 25 at index 3 No swap needed (already in position): [11, 12, 22, 25, 64]

The array is now sorted.

Here’s the pseudocode representation:

procedure selectionSort(array):
    n = length(array)
    
    for i from 0 to n-2:
        minIndex = i
        
        for j from i+1 to n-1:
            if array[j] < array[minIndex]:
                minIndex = j
        
        if minIndex != i:
            swap array[i] and array[minIndex]
    
    return array

Implementation

Let’s translate this into working code. I’ll provide implementations in Python and JavaScript with clear variable naming.

Python Implementation

def selection_sort(arr):
    """
    Sort array in-place using selection sort algorithm.
    Returns the sorted array for convenience.
    """
    n = len(arr)
    
    # Iterate through each position that needs to be filled
    for current_position in range(n - 1):
        # Assume the current position has the minimum value
        min_index = current_position
        
        # Search the unsorted portion for a smaller element
        for search_index in range(current_position + 1, n):
            if arr[search_index] < arr[min_index]:
                min_index = search_index
        
        # Swap only if we found a smaller element
        if min_index != current_position:
            arr[current_position], arr[min_index] = arr[min_index], arr[current_position]
    
    return arr


# Usage example
data = [64, 25, 12, 22, 11]
sorted_data = selection_sort(data)
print(sorted_data)  # Output: [11, 12, 22, 25, 64]

JavaScript Implementation

function selectionSort(arr) {
    const n = arr.length;
    
    // Iterate through each position that needs to be filled
    for (let currentPosition = 0; currentPosition < n - 1; currentPosition++) {
        // Assume the current position has the minimum value
        let minIndex = currentPosition;
        
        // Search the unsorted portion for a smaller element
        for (let searchIndex = currentPosition + 1; searchIndex < n; searchIndex++) {
            if (arr[searchIndex] < arr[minIndex]) {
                minIndex = searchIndex;
            }
        }
        
        // Swap only if we found a smaller element
        if (minIndex !== currentPosition) {
            [arr[currentPosition], arr[minIndex]] = [arr[minIndex], arr[currentPosition]];
        }
    }
    
    return arr;
}

// Usage example
const data = [64, 25, 12, 22, 11];
const sortedData = selectionSort(data);
console.log(sortedData);  // Output: [11, 12, 22, 25, 64]

Both implementations modify the array in-place and return it for convenience. The conditional swap check (if minIndex != currentPosition) is an optimization that avoids unnecessary swap operations when the minimum element is already in the correct position.

Time and Space Complexity Analysis

Time Complexity: O(n²)

Selection sort has the same time complexity in all cases—best, average, and worst:

  • Best case: O(n²) — even if the array is already sorted
  • Average case: O(n²)
  • Worst case: O(n²)

Why is this consistent? The algorithm always performs the same number of comparisons regardless of input order. For an array of n elements:

  • First pass: n-1 comparisons
  • Second pass: n-2 comparisons
  • Third pass: n-3 comparisons
  • …and so on

Total comparisons: (n-1) + (n-2) + … + 1 = n(n-1)/2 = O(n²)

This predictability is actually a characteristic worth noting. Unlike insertion sort, which can finish in O(n) time on nearly-sorted data, selection sort provides no such optimization. The algorithm is oblivious to the existing order.

Space Complexity: O(1)

Selection sort is an in-place algorithm. It only requires a constant amount of additional memory for:

  • Loop counters
  • The minimum index variable
  • Temporary storage during swaps

This makes it attractive when memory is severely constrained.

Advantages and Disadvantages

Advantages

Minimal swaps: Selection sort performs at most n-1 swaps. Each element is moved at most once to its final position. This is significant when write operations are expensive (flash memory, network writes, or moving physical objects).

Simplicity: The algorithm is easy to understand, implement, and debug. There are no edge cases or special conditions to handle.

In-place operation: No additional data structures or significant memory allocation required.

Predictable performance: The execution time is consistent regardless of input order, which can be valuable in real-time systems where worst-case performance matters.

Disadvantages

Poor time complexity: O(n²) makes it impractical for large datasets. Sorting 10,000 elements requires approximately 50 million comparisons.

No early termination: Cannot detect if the array becomes sorted before completing all passes.

Not stable: Equal elements may not preserve their relative order. If you’re sorting objects by one field while maintaining order from a previous sort, selection sort will break that.

Not adaptive: Provides no performance benefit for partially sorted data.

Selection Sort vs. Other Sorting Algorithms

Let’s compare selection sort with its peers and see where it fits in the sorting algorithm landscape.

Algorithm Time (Best) Time (Worst) Space Stable Adaptive
Selection Sort O(n²) O(n²) O(1) No No
Bubble Sort O(n) O(n²) O(1) Yes Yes
Insertion Sort O(n) O(n²) O(1) Yes Yes
Merge Sort O(n log n) O(n log n) O(n) Yes No
Quick Sort O(n log n) O(n²) O(log n) No No

Here’s a practical comparison showing swap counts between selection sort and bubble sort:

def selection_sort_with_metrics(arr):
    """Selection sort that counts swaps."""
    arr = arr.copy()
    swaps = 0
    n = len(arr)
    
    for i in range(n - 1):
        min_idx = i
        for j in range(i + 1, n):
            if arr[j] < arr[min_idx]:
                min_idx = j
        if min_idx != i:
            arr[i], arr[min_idx] = arr[min_idx], arr[i]
            swaps += 1
    
    return arr, swaps


def bubble_sort_with_metrics(arr):
    """Bubble sort that counts swaps."""
    arr = arr.copy()
    swaps = 0
    n = len(arr)
    
    for i in range(n):
        for j in range(0, n - i - 1):
            if arr[j] > arr[j + 1]:
                arr[j], arr[j + 1] = arr[j + 1], arr[j]
                swaps += 1
    
    return arr, swaps


# Test with reverse-sorted array (worst case for swaps)
test_data = [10, 9, 8, 7, 6, 5, 4, 3, 2, 1]

_, selection_swaps = selection_sort_with_metrics(test_data)
_, bubble_swaps = bubble_sort_with_metrics(test_data)

print(f"Selection sort swaps: {selection_swaps}")  # Output: 5
print(f"Bubble sort swaps: {bubble_swaps}")        # Output: 45

Selection sort performs 5 swaps while bubble sort performs 45—a 9x difference. This gap grows with array size.

Practical Applications and When to Use It

Despite its limitations, selection sort has legitimate use cases:

Memory-constrained embedded systems: When you have kilobytes of RAM and need to sort a small array, selection sort’s O(1) space and simple implementation make it practical.

Expensive write operations: Sorting data on flash memory or EEPROM where write cycles are limited and costly. Selection sort’s minimal swaps extend hardware lifespan.

Small datasets: For arrays under 20-30 elements, the overhead of more sophisticated algorithms may not justify the complexity. Selection sort is fast enough and simpler to verify.

Educational purposes: Learning selection sort builds intuition for algorithm analysis and comparison-based sorting concepts.

Sorting physical objects: When you’re literally moving items (warehouse inventory, physical cards), minimizing moves matters more than minimizing comparisons.

When to Avoid It

For general-purpose sorting in production code, use your language’s built-in sort (typically Timsort or Introsort). For specific needs:

  • Large datasets: Use merge sort, quicksort, or heapsort
  • Nearly sorted data: Use insertion sort
  • Stability required: Use merge sort or Timsort
  • Memory constrained but need better performance: Use heapsort (O(n log n) with O(1) space)

Selection sort is a tool for specific situations, not a general solution. Understand its characteristics, recognize when those characteristics align with your constraints, and choose accordingly.

Liked this? There's more.

Every week: one practical technique, explained simply, with code you can use immediately.