一、选择排序的基本概念
选择排序(Selection Sort)是一种简单的排序算法。它的基本思想是:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,然后,再从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。
二、选择排序的过程
- 第一轮排序
- 假设我们有一个数组
arr = [5, 4, 3, 2, 1]
。在第一轮排序中,我们需要在整个数组中找到最小的元素。通过比较,我们发现最小的元素是1
。 - 然后将
1
与数组的第一个元素5
交换位置,此时数组变为[1, 4, 3, 2, 5]
。
- 假设我们有一个数组
- 第二轮排序
- 现在我们只需要考虑数组中除了第一个元素(已经排好序)之外的部分,即
[4, 3, 2, 5]
。在这个子数组中,最小的元素是2
。 - 将
2
与子数组的第一个元素4
交换位置,数组变为[1, 2, 3, 4, 5]
。
- 现在我们只需要考虑数组中除了第一个元素(已经排好序)之外的部分,即
- 后续排序过程
- 继续这个过程,每次都在未排序的部分中找到最小的元素,并将其交换到正确的位置,直到整个数组都排序完成。
三、选择排序的时间复杂度
- 最坏情况和平均情况
- 时间复杂度为\(O(n^2)\)。在最坏的情况下,每次都需要比较剩余未排序元素中的所有元素来找到最小(大)的那个。例如,对于一个逆序排列的数组,如
[n, n - 1, n - 2,..., 1]
,在第一轮需要比较n - 1
次找到最小元素,第二轮需要比较n - 2
次,以此类推,总的比较次数为\(\sum_{i = 1}^{n-1}i=\frac{n(n - 1)}{2}\),当\(n\)很大时,其时间复杂度近似为\(O(n^2)\)。 - 平均情况也是\(O(n^2)\),因为无论数组的初始顺序如何,选择排序都需要进行大致相同数量的比较操作来确定最小(大)元素。
- 时间复杂度为\(O(n^2)\)。在最坏的情况下,每次都需要比较剩余未排序元素中的所有元素来找到最小(大)的那个。例如,对于一个逆序排列的数组,如
- 最好情况
- 时间复杂度仍然是\(O(n^2)\)。即使数组已经是有序的,选择排序也会遍历剩余的未排序元素来确认它们已经是有序的,不会像某些其他排序算法(如冒泡排序在最好情况下时间复杂度可以为\(O(n)\))那样提前结束排序过程。
四、选择排序的空间复杂度
- 空间复杂度
- 选择排序是一种原地排序算法,它的空间复杂度为\(O(1)\)。这是因为它只需要有限的额外空间来进行元素的交换和比较,不需要额外的数据结构来存储整个数组的副本等,只是在原数组的基础上通过交换元素的位置来实现排序。
五、选择排序的稳定性
- 稳定性定义
- 稳定排序是指在排序过程中,相等的元素在排序前后的相对位置不变。
- 选择排序的不稳定性
- 选择排序是不稳定的排序算法。例如,对于数组
[5, 4, 5, 2, 3]
,在第一轮排序中,会将最小的元素2
与第一个5
交换位置,这样就改变了两个5
元素的相对位置,导致排序结果可能不符合稳定排序的要求。
- 选择排序是不稳定的排序算法。例如,对于数组
六、选择排序的代码实现(以升序为例,使用Python)
def selection_sort(arr):n = len(arr)for i in range(n):min_idx = ifor j in range(i + 1, n):if arr[j] < arr[min_idx]:min_idx = jarr[i], arr[min_idx] = arr[min_idx], arr[i]return arr
- 代码解释
- 外层循环
for i in range(n)
控制排序的轮数,总共需要n - 1
轮排序就可以将n
个元素排好序。 - 在内层循环
for j in range(i + 1, n)
中,从i+1
位置开始到数组末尾,寻找最小的元素的索引min_idx
。 - 找到最小元素的索引后,将
arr[i]
和arr[min_idx]
交换位置,这样就将最小元素放到了已排序部分的末尾。
- 外层循环
七、选择排序的应用场景
- 小规模数据排序
- 由于其简单易懂的实现方式,对于小规模的数据(例如,数据量小于100),选择排序可以是一个不错的选择。在这种情况下,它的\(O(n^2)\)时间复杂度不会导致性能问题过于严重。
- 作为其他复杂算法的基础组件
- 在一些更复杂的算法中,可能会用到选择排序的思想来处理局部数据的排序或者预排序,例如在某些贪心算法或者分治算法的子过程中,选择排序的简单性可以帮助快速实现局部的排序需求。