选择排序(Selection Sort)

每次从未排序部分选择最小值,放到已排序部分的末尾。简单朴素但存在三个主要缺陷:不稳定、无法利用有序性、无法优化。

定义

选择排序(Selection Sort)是最简单朴素的排序算法之一。其核心思想是:

每次从右侧未排序部分选择最小值,放到左侧已排序部分的末尾。

核心特性

特性 说明
时间复杂度 O(n²),无论输入如何都是 O(n²)
空间复杂度 O(1),原地排序
稳定性 ❌ 不稳定(交换可能改变相同元素相对位置)
是否原地 ✅ 是
突破性 ❌ 无法突破 O(n²)

基本原理

算法过程

  1. 初始状态:[0, sortedIndex) 为已排序部分,sortedIndex 初始为 0
  2. 每次迭代:在 [sortedIndex, n) 未排序部分找到最小值
  3. 将最小值与 nums[sortedIndex] 交换
  4. sortedIndex++,扩大已排序部分
void sort(int[] nums) {
    int n = nums.length;
    int sortedIndex = 0;
    while (sortedIndex < n) {
        // 在 [sortedIndex, n) 中找到最小值的索引
        int minIndex = sortedIndex;
        for (int i = sortedIndex + 1; i < n; i++) {
            if (nums[i] < nums[minIndex]) {
                minIndex = i;
            }
        }
        // 将最小值交换到 sortedIndex 位置
        int tmp = nums[sortedIndex];
        nums[sortedIndex] = nums[minIndex];
        nums[minIndex] = tmp;

        sortedIndex++;
    }
}

三个主要缺陷

  1. 不稳定:交换操作可能改变相同元素的相对位置
  2. 无法利用有序性:无论数组是否有序,都要完整执行 O(n²) 次操作
  3. 无法优化:尝试用 suffixMin 预处理来优化,但遇到交换导致的不稳定性问题

与类似概念对比

维度 选择排序 插入排序 冒泡排序
时间复杂度 O(n²) O(n²) O(n²)
空间复杂度 O(1) O(1) O(1)
稳定性 ❌ 不稳定 ✅ 稳定 ✅ 稳定
提前终止 ❌ 不支持 ✅ 支持(利用有序性) ✅ 支持
有序度利用 ❌ 不利用 ✅ 高效 ⚠️ 利用但效率低
优化潜力 ❌ 无 ✅ 有(希尔排序等) ⚠️ 有限

时间复杂度/性能

场景 时间复杂度 说明
最好情况 O(n²) 即使已排序也要完整执行
最坏情况 O(n²) 完全逆序
平均情况 O(n²) 操作次数固定 = n(n-1)/2
与输入关系 无关 无论输入如何都是 O(n²)

注意事项

  • 交换导致不稳定swap(minIndex, sortedIndex) 可能改变相同元素的相对位置
  • 无法利用有序性:即使数组已经有序,仍然执行完整的 O(n²) 次比较
  • 优化尝试失败:尝试用 suffixMin 数组预处理来减少比较次数,但交换操作破坏了 suffixMin 的正确性,且仍然不稳定

应用场景

选择排序由于效率低下,实际应用中很少直接使用,但作为教学示例非常重要:

  • 理解基础排序思想
  • 作为优化起点(插入排序、冒泡排序都是其改进)
  • 理解算法稳定性和优化限制

经典算法

  • 插入排序:选择排序的改进版,通过反向思维突破限制
  • 冒泡排序:选择排序的另一种改进,通过交换相邻逆序对实现稳定排序
  • 希尔排序:插入排序的进一步改进,突破 O(n²)

相关概念


Interactive Graph