【挑战程序设计竞赛 | 笔记】冒泡排序:Bubble Sort

59 阅读3分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 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 的末尾 N1N-1 开始,减少到 i+1 结束

分析

在冒泡排序法中:

  • 稳定性:仅对数组中的相邻元素进行比较、交换,键相同的元素不会改变顺序。因此冒泡排序法也属于一种稳定排序的算法。
  • 时间复杂度:O(n2)O(n^2)

记录:

  • 如果将交换条件改为:if(A[j-1] >= A[j]),那么算法将失去稳定性。
  • 冒泡排序法中的交换次数又称为反序数或逆序数,可用于体现数列的错乱程度。