选择排序

raw/articles/2026/05/选择排序

一句话总结

选择排序是最简单朴素的排序算法,时间复杂度 O(n²),不是稳定排序,其他基础排序算法都是基于选择排序的优化。

核心要点

1. 算法思路

先遍历数组找到最小值,与第一个元素交换;再遍历剩余部分找到次小值,与第二个元素交换;以此类推直到整个数组有序。

2. 时间复杂度与优化尝试

  • 时间复杂度:O(n²),嵌套循环执行 n²/2 次
  • 特点:即使数组已有序,仍执行相同次数,原始数据有序度不影响时间复杂度
  • 优化尝试:尝试用 suffixMin 数组预计算后缀最小值,但因交换操作导致 nums 变化,预计算失效,无法优化

3. 排序稳定性

  • 不是稳定排序:交换操作会打乱相同元素的相对位置
  • 原因:第一次寻找最小值时就可能交换,破坏相同元素的原始顺序
  • 优化思考:作者提出思考方向,后续排序算法会尝试解决

基本原理

选择排序的核心:每次选择未排序部分的最小值,放到已排序部分的末尾。

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++;
    }
}

时间复杂度

操作 时间复杂度 说明
排序 O(n²) 嵌套循环,等差数列求和 n²/2
空间 O(1) 原地排序,仅用常数个变量

代码实现

关键代码片段

// 计算后缀最小值的尝试(虽然最终无法用于优化选择排序)
int[] suffixMin = new int[nums.length];
suffixMin[nums.length - 1] = nums[nums.length - 1];
for (int i = nums.length - 2; i >= 0; i--) {
    suffixMin[i] = Math.min(nums[i], suffixMin[i + 1]);
}

实现要点

  • 使用 sortedIndex 作为已排序/未排序的分割线
  • 每次遍历未排序部分找最小值索引
  • 交换最小值与分割线位置的元素
  • 分割线后移一位

对比分析

维度 选择排序 说明
时间复杂度 O(n²) 基础排序算法中最慢的一档
空间复杂度 O(1) 原地排序
稳定性 不稳定 交换操作破坏相同元素相对顺序
适用场景 小规模数据 大规模数据应使用更高效的排序算法

应用场景

  • 小规模数据排序
  • 教学演示排序算法基础思想
  • 作为更复杂排序算法的对比基准

验证方法

  • 可通过 LeetCode 912「排序数组」验证(但会超时,仅逻辑正确)
  • 可通过简单测试代码验证正确性

相关题目

平台 题目 难度
LeetCode 912. Sort an Array Medium
力扣 912. 排序数组 中等

相关概念

相关实体

  • labuladong:算法学习平台 labuladong.online 作者

总结

选择排序虽然简单,但蕴含了重要的算法思维:通过尝试优化(如 suffixMin 预计算)理解算法的局限性。交换操作既导致不稳定,也阻碍了优化。后续排序算法(如插入排序、冒泡排序、快速排序等)将在选择排序的基础上改进。

延伸资源


Interactive Graph