Menu
CoddyTech

选择排序

最后更新

选择排序把数组分成左边的有序区和右边的无序区。每一轮扫描无序区,找出最小的元素,再把它交换到无序区的第一个位置,有序区就增加一个元素。点击上方的“播放”,观看扫描和交换的过程,也可以一次只看一次比较。

无论输入是什么,选择排序的比较次数都相同,但它最多只做 n-1 次交换,比冒泡排序少得多。在写入代价很高时,这一点可能很重要。

时间复杂度与空间复杂度

情况复杂度说明
最好情况O(n²)即使已经有序也要比较
平均情况O(n²)随机顺序
最坏情况O(n²)逆序排列
空间O(1)原地排序
稳定性不稳定交换可能打乱相等元素的顺序

分步说明

步骤发生了什么
1把整个数组看作无序区。
2扫描无序区,找出最小的元素。
3把这个最小值交换到无序区的第一个位置。
4把分界线向右移动一位(这个位置已经排好)。
5重复以上步骤,直到无序区只剩一个元素。

实例演示

对 [5, 2, 4, 1] 排序:

轮次数组操作
开始[5, 2, 4, 1]整个数组都是无序的。
1[1, 2, 4, 5]扫描 [5, 2, 4, 1],最小值是下标 3 处的 1;把它与下标 0 交换。
2[1, 2, 4, 5]扫描 [2, 4, 5],最小值 2 已经在下标 1 处;与自身交换。
3[1, 2, 4, 5]扫描 [4, 5],最小值 4 已经在下标 2 处;无需移动。
完成[1, 2, 4, 5]只剩下 5,它已经在正确的位置上。

什么时候用选择排序

适合使用不适合使用
写入代价很高:它最多只做 n-1 次交换。数组很大:O(n²) 次比较会成为主要开销。
你需要一种简单、容易实现的原地排序。你需要稳定排序,保留相等键的原有顺序。
内存紧张:它只用 O(1) 的额外空间。数据几乎有序:它不能像插入排序那样提前结束。
数据量很小,而且看重性能的可预测性。看重吞吐量:快速排序等 O(n log n) 的排序算法要快得多。

Selection Sort 代码

简洁、可运行的 Selection Sort 实现,提供 Python, JavaScript, Java, C++, C, Pseudocode 版本。选择一种语言,复制代码,或在 Coddy 在线编辑器中直接打开。

Selection Sort 的 Python 代码

Python
1def selection_sort(a):2    n = len(a)3    for i in range(n - 1):4        # Find the smallest element in the unsorted tail5        min_idx = i6        for j in range(i + 1, n):7            if a[j] < a[min_idx]:8                min_idx = j9        a[i], a[min_idx] = a[min_idx], a[i]10    return a11
12
13nums = [64, 25, 12, 22, 11]14print("Before:", nums)15selection_sort(nums)16print("After: ", nums)
在 Python 在线编辑器中运行这段代码

选择排序常见问题

选择排序的时间复杂度是多少?
选择排序在所有情况下(最好、平均和最坏)都是 O(n²),因为每找一个最小值都要扫描整个无序区。它只用 O(1) 的额外空间。
选择排序是稳定的吗?

标准的原地版本不稳定,因为把远处的最小值交换过来时,可能让一个相等的元素越过另一个。稳定的变体确实存在,但需要用移动代替交换。

选择排序什么时候有用?
当写入内存的代价很高时它很有用,因为它最多只做 n-1 次交换,这是移动元素的比较排序所能做到的最少次数。
选择排序和冒泡排序有什么区别?
两者都是 O(n²) 的比较排序,但选择排序最多做 n-1 次交换,而冒泡排序最多可能做 O(n²) 次交换。冒泡排序还能发现数组已经有序并提前停止,而选择排序总是跑完全部轮次。
该用选择排序还是插入排序?
大多数情况下优先用插入排序:它是稳定的,在接近有序的数据上只需 O(n),平均速度也更快。只有当减少写入次数是首要目标时才选选择排序,因为它保证最多只做 n-1 次交换。
为什么选择排序在有序数组上也是 O(n²)?
选择排序不扫描无序区的其余部分,就无法知道某个元素是否已经是最小值,所以无论输入顺序如何,它每一轮都要做完所有比较。这意味着它的最好情况和最坏情况一样,都是 O(n²);而插入排序或冒泡排序则可以提前结束。
Coddy 编程语言插图

在 Coddy 掌握算法

开始