开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 27 天,点击查看活动详情
认识排序
排序就是将数据按一定顺序重新排列,即将数据按照键重新排列为升序( 从小到大 ) 或降序( 从大到小 ) 的处理。
比如:
- 一个数列为:
A = {5, 1, 3, 4, 2}
- 按升序排列:
A = {1, 2, 3, 4, 5}
- 按降序排列:
A = {5, 4, 3, 2, 1}
我们通过数组来管理这些数列形式的数据,并通过循环处理完成数据的交换和移动,最终实现排序。
我们在设计或者选择算法时,不仅需要考虑复杂度,还要考虑该排序算法是否稳定。
稳定排序指的是:当需要排序的数据中存在 2 个或者 2 个以上的相等值时,这些元素在排序前后的顺序不变。
选择排序 | Selection Sort
思想
选择排序法是一种非常直观的算法,它会在每个计算步骤中选出一个最小值,进而完成排序。
与前面一样,在选择排序算法中的各个步骤,数组也分为两部分:成 “已排序部分” 和 “未排序部分” 。
具体的,选择排序算法记录如下:
-
重复 次下述处理:
- 从未排序部分当中选择最小的那个值,取它的下标
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
号元素中最小值的位置
分析
在选择排序法中:
- 稳定性:由于选择排序法会直接交换两个不相邻的元素,所以属于不稳定的排序算法。
- 时间复杂度:
记录:
- 与冒泡排序法从局部入手减少逆序元素不同,选择排序法放眼大局逐个选择最小值, 二者思路大不相同。但是它们的相同点在于,都是有着 “通过
i
次外层循环,从数据中顺次求岀i
个最小值” 的相同特征。相对地,插入排序法是通过i
次外层循环,直接将原数组的i
个元素重新排序。- 不含有
flag
的冒泡排序法和选择排序法不依赖数据,即比较运算的次数不受输入数据影响,而插入算法在执行时却依赖数据,处理某些数据时具有很高的效率。