开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 28 天,点击查看活动详情
认识排序
排序就是将数据按一定顺序重新排列,即将数据按照键重新排列为升序( 从小到大 ) 或降序( 从大到小 ) 的处理。
比如:
- 一个数列为:
A = {5, 1, 3, 4, 2}
- 按升序排列:
A = {1, 2, 3, 4, 5}
- 按降序排列:
A = {5, 4, 3, 2, 1}
我们通过数组来管理这些数列形式的数据,并通过循环处理完成数据的交换和移动,最终实现排序。
我们在设计或者选择算法时,不仅需要考虑复杂度,还要考虑该排序算法是否稳定。
稳定排序指的是:当需要排序的数据中存在 2 个或者 2 个以上的相等值时,这些元素在排序前后的顺序不变。
冒泡排序 | Bubble Sort
思想
顾名思义,冒泡排序法就是让数组元素像水中的气泡一样逐渐上浮,进而达到排序的目的。
与插入排序算法一样,在冒泡排序算法中的各个步骤,数组也分为两部分:成 “已排序部分” 和 “未排序部分” 。
具体的,冒泡排序算法记录如下:
-
重复执行以下处理(直到数组中不包含顺序相反的相邻元素):
- 从数组末尾开始,往前比较相邻两个元素
- 如果大小关系相反,则交换位置
如果我们想要升序效果,那么大小关系相反指的是:前一个元素 大于 后一个元素
代码
void bubbleSort(int A[], int n) {
bool flag = 1; // 存在顺序相反的相邻元素
while(flag) {
flag = 0;
for(int j = n-1; j>=1; j--) {
if(A[j-1] > A[j]) {
swap(A[j-1], A[j]);
flag = 1;
}
}
}
}
在上述代码中:
- 数据从数组开头逐一完成排序(逐一确定已排序部分末尾要追加的元素)
- 每执行一次
for
循环,已排序部分就会添加一个元素。 - 这样一来,外层循环最多需要执行 n 次。
上述代码还可以进行优化:发挥外层循环变量 i
的作用。
如下所示:
void bubbleSort(int A[], int n) {
bool flag = 1; // 存在顺序相反的相邻元素
for(int i = 0; flag; i++) {
flag = 0;
for(int j = n-1; j>=i+1; j--) {
if(A[j-1] > A[j]) {
swap(A[j-1], A[j]);
flag = 1;
}
}
}
}
变量记录:
i
:循环变量,表示未排序部分的开头元素 , 从数组开头向末尾移动j
:循环变量, 用于对未排序部分中的相邻元素两两比较, 从 A 的末尾 开始,减少到i+1
结束
分析
在冒泡排序法中:
- 稳定性:仅对数组中的相邻元素进行比较、交换,键相同的元素不会改变顺序。因此冒泡排序法也属于一种稳定排序的算法。
- 时间复杂度:
记录:
- 如果将交换条件改为:
if(A[j-1] >= A[j])
,那么算法将失去稳定性。- 冒泡排序法中的交换次数又称为反序数或逆序数,可用于体现数列的错乱程度。