【挑战程序设计竞赛 | 笔记】选择排序:Selection Sort

46 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天,点击查看活动详情

认识排序

排序就是将数据按一定顺序重新排列,即将数据按照键重新排列为升序( 从小到大 ) 或降序( 从大到小 ) 的处理。

比如:

  • 一个数列为:A = {5, 1, 3, 4, 2}
  • 按升序排列:A = {1, 2, 3, 4, 5}
  • 按降序排列:A = {5, 4, 3, 2, 1}

我们通过数组来管理这些数列形式的数据,并通过循环处理完成数据的交换和移动,最终实现排序。

我们在设计或者选择算法时,不仅需要考虑复杂度,还要考虑该排序算法是否稳定。

稳定排序指的是:当需要排序的数据中存在 2 个或者 2 个以上的相等值时,这些元素在排序前后的顺序不变。

选择排序 | Selection Sort

思想

选择排序法是一种非常直观的算法,它会在每个计算步骤中选出一个最小值,进而完成排序。

与前面一样,在选择排序算法中的各个步骤,数组也分为两部分:成 “已排序部分”“未排序部分”

具体的,选择排序算法记录如下:

  • 重复 n1n-1 次下述处理:

    • 从未排序部分当中选择最小的那个值,取它的下标 minj
    • minj 位置上的元素 和 未排序部分的起始元素 进行交换

简单来说,就是在每一轮 i 的循环中,通过 j 自增,依次遍历 A[i]A[n-1] ,从而确定 minj。确定完 minj后,让起始元素 A[i] 与最小值元素 A[minj] 进行交换即可。

代码

我们利用两个 for 循环来实现选择排序算法。

在初始状态下,所有元素均处于未排序部分,外层循环从 A[0] 遍历到 A[n-1]

内层循环的作用是寻找到未排序部分的最小值,然后将这个值和当前外层循环下的数组元素进行交换。

交换完成后,此时外层循环变量 i 指向的部分属于已排序部分,接着外层循环继续遍历下一个数组元素,以此类推,后续的步骤同理。

因此代码如下:

void selectionSort(int A[], int n) {
    for(int i = 0; i<n; i++) {
        int minj = i;
        for(int j = i; j<n; j++) {
            if(A[j] < A[minj]) {
                minj = j;
            }
        }
        swap(A[i], A[minj]);
    }
}

变量记录:

  • i:外层循环变量 , 表示未排序部分的开头元素 , 从数组开头向末尾移动
  • j:内层循环变量, 用来查找未排序部分中最小值的下标位置,即寻找 minj
  • minj:各轮循环处理中,第 i 号 到 第 n-1 号元素中最小值的位置

分析

在选择排序法中:

  • 稳定性:由于选择排序法会直接交换两个不相邻的元素,所以属于不稳定的排序算法。
  • 时间复杂度:O(n2)O(n^2)

记录:

  • 与冒泡排序法从局部入手减少逆序元素不同,选择排序法放眼大局逐个选择最小值, 二者思路大不相同。但是它们的相同点在于,都是有着 “通过i 次外层循环,从数据中顺次求岀 i 个最小值” 的相同特征。相对地,插入排序法是通过 i 次外层循环,直接将原数组的 i 个元素重新排序。
  • 不含有 flag 的冒泡排序法和选择排序法不依赖数据,即比较运算的次数不受输入数据影响,而插入算法在执行时却依赖数据,处理某些数据时具有很高的效率。