选择排序 (Selection Sort)

15 阅读3分钟

1. 原理解析

假设你面前有一筐大小不一的苹果,你想把它们按从小到大排成一排。 你会怎么做?

  • 你肯定是一眼扫过去,挑出一个最小的,放在第一位。
  • 然后从剩下的苹果里,再挑出一个最小的,放在第二位。
  • 以此类推,直到挑完所有的苹果。

这就是选择排序的灵魂:每次在未排序的序列中,选出最小(或最大)的那个元素,放到已排序序列的末尾。

2. 使用场景

  • 数据量极小:对于几条、几十条数据,写起来快,逻辑简单,不用引入复杂的算法。
  • 写入成本极高的设备:在所有 O(N²) 的排序算法中,选择排序有一个独特的优势——它的交换次数最少(最多只有 N-1 次交换)。如果某个硬件设备往内存里写数据的代价非常昂贵,选择排序比冒泡排序和插入排序都要好得多。

3. 代码实战

Java 版本

public class SelectionSort {
    public static void sort(int[] arr) {
        if (arr == null || arr.length < 2) return;
        
        int n = arr.length;
        // i 代表当前正在确定(选出最小值)的位置
        for (int i = 0; i < n - 1; i++) {
            // 假设当前 i 位置就是剩下的元素中最小的
            int minIndex = i;
            
            // 从 i 的下一个位置开始,去剩下的元素里找有没有更小的
            for (int j = i + 1; j < n; j++) {
                if (arr[j] < arr[minIndex]) {
                    minIndex = j; // 如果发现更小的,记录它的下标!
                }
            }
            
            // 找了一圈,如果真正的最小值不在初始假设的 i 位置,就把它交换过来
            if (minIndex != i) {
                int temp = arr[i];
                arr[i] = arr[minIndex];
                arr[minIndex] = temp;
            }
        }
    }
}

Python 版本

def selection_sort(arr):
    if not arr or len(arr) < 2:
        return
        
    n = len(arr)
    # i 代表当前正在确定(选出最小值)的位置
    for i in range(n - 1):
        # 假设当前 i 位置就是剩下的元素中最小的
        min_index = i
        
        # 从 i+1 开始遍历剩下的元素找更小的
        for j in range(i + 1, n):
            if arr[j] < arr[min_index]:
                min_index = j # 发现更小的,记录下标
                
        # 找了一圈,如果最小值不是最初假设的 i,就交换
        if min_index != i:
            arr[i], arr[min_index] = arr[min_index], arr[i]

4. 核心难点与易错点详解

  • 找最小值的范围与边界:外层循环变量设为 i,代表当前要确定的是第 i 个位置上的元素。那么内层寻找最小值的循环变量 j,必须从 i + 1 开始找,因为前 i 个元素(也就是你之前已经挑出来的更小的苹果)已经排好序了。
  • 手里拿着的是“编号”,而不是“实体苹果”:在挑最小苹果的过程中,我们用一个变量 minIndex 记住当前最小苹果所在的位置(下标),而不是把苹果的值拿在手里。等剩下的一筐苹果都看了一遍后,再把 minIndex 位置上的苹果和第 i 个位置上的苹果交换。

5. 复杂度分析

  • 时间复杂度:O(N²)。无论数组原来是乱序还是已经排好序的,每次找最小值都必须老老实实把剩下的元素全看一遍。所以最好、最坏、平均时间复杂度全都是 O(N²)。
  • 空间复杂度:O(1)。只需要几个常数级别的额外变量(如 minIndextemp)。
  • 稳定性:不稳定。举个例子:序列 [5, 5, 2]。第一次找最小值找到 2,和第一个 5 交换,变成了 [2, 5, 5]。原本在前面的那个 5 被换到了后面,相对顺序被破坏了。