排序算法:冒泡排序法

文章目录

一、冒泡排序简介

冒泡排序(Bubble Sort)是一种简单的排序算法,它重复地遍历待排序的数列,依次比较相邻的元素,并根据需要交换它们的位置。这样较大的元素像气泡一样逐渐"冒泡"到数列的末端,最终将整个数列排好顺序。

关键点:

  1. 比较相邻元素:每次比较两个相邻元素,如果它们的顺序错误(通常是前一个比后一个大),就交换它们。
  2. 冒泡过程:每一轮遍历会将当前未排序部分的最大元素推到最右侧。
  3. 逐步缩小比较范围:每次遍历完成后,右边的部分已经是排序好的,因此可以忽略已排序的元素,逐渐缩小比较范围。

冒泡排序的步骤:

  1. 从第一个元素开始,依次比较相邻元素。
  2. 若前面的元素比后面的元素大,则交换它们的位置。
  3. 这样大的元素不断"冒泡"到数组的末尾。
  4. 重复这个过程,直到数组完全有序。

二、冒泡排序演示排序过程

示例:排序数组 [5, 3, 8, 4, 2]

初始状态:

cpp 复制代码
[5, 3, 8, 4, 2]

第一轮遍历:

  • 比较 5 和 3,交换它们,得到 [3, 5, 8, 4, 2]
  • 比较 5 和 8,不交换,得到 [3, 5, 8, 4, 2]
  • 比较 8 和 4,交换它们,得到 [3, 5, 4, 8, 2]
  • 比较 8 和 2,交换它们,得到 [3, 5, 4, 2, 8]

第二轮遍历:

  • 比较 3 和 5,不交换,得到 [3, 5, 4, 2, 8]
  • 比较 5 和 4,交换它们,得到 [3, 4, 5, 2, 8]
  • 比较 5 和 2,交换它们,得到 [3, 4, 2, 5, 8]

第三轮遍历:

  • 比较 3 和 4,不交换,得到 [3, 4, 2, 5, 8]
  • 比较 4 和 2,交换它们,得到 [3, 2, 4, 5, 8]

第四轮遍历:

  • 比较 3 和 2,交换它们,得到 [2, 3, 4, 5, 8]

最终排序结果:

cpp 复制代码
[2, 3, 4, 5, 8]

三、冒泡排序的时间复杂度分析

冒泡排序的时间复杂度分析主要依赖于其在最坏情况下、最好情况下和平均情况下的表现。

1. 最坏情况时间复杂度

最坏情况发生在输入数组是逆序排列的情况下。在这种情况下,冒泡排序需要进行最大次数的比较和交换操作。

排序过程:

第一轮遍历会比较 n - 1 对元素,第二轮遍历比较 n - 2 对元素,依此类推。因此,冒泡排序需要进行 n-1 轮遍历,每一轮遍历会进行最多的交换操作。

计算比较次数:

  • 第一轮:n-1 次比较。
  • 第二轮:n-2 次比较。
  • 第三轮:n-3 次比较。
  • ...
  • 最后一轮:1 次比较。

因此,所有比较的总数为:

这就是一个等差数列的求和,即 O(n²)。
最坏情况时间复杂度:O(n²)

2.最好情况时间复杂度

最好情况发生在输入数组已经是有序的情况下。为了优化冒泡排序,我们可以加入一个标志位来检查每一轮是否发生了交换。如果没有发生交换,说明数组已经排好序,可以提前结束排序。

排序过程:

在最好情况下,冒泡排序将进行 n-1 次比较,但由于没有交换,算法会在第一次遍历后提前结束。

计算比较次数:

  • 第一轮:n-1 次比较。
  • 第二轮:没有交换,提前结束。

因此,最好情况下只需要 O(n) 次比较。
最好情况时间复杂度:O(n)

3.平均情况时间复杂度

平均情况指的是输入数组中的元素顺序是随机的。计算平均时间复杂度时,我们假设每一轮排序的交换和比较大致是均匀分布的。

排序过程:

平均情况下,冒泡排序会执行与最坏情况类似的操作,但有可能发生一些交换。虽然交换次数较少,但每一轮的比较次数仍然接近最坏情况的比较次数。

计算过程与最坏情况类似:

  • 第一轮:n-1 次比较。
  • 第二轮:n-2 次比较。
  • 第三轮:n-3 次比较。
  • ...
  • 最后一轮:1 次比较。

总比较次数为:

因此,平均时间复杂度 仍然是 O(n²)。
平均情况时间复杂度:O(n²)

4. 优化后的冒泡排序

如果我们优化冒泡排序,使用一个布尔变量 swapped 来标记每一轮是否进行了交换:

  • 如果某一轮没有交换,说明数组已经有序,可以提前终止排序,减少不必要的比较。
  • 这种优化可以将最佳情况的时间复杂度减少到 O(n),但最坏情况和平均情况的时间复杂度仍然是 O(n²)。

5.空间复杂度

冒泡排序是原地排序算法,不需要额外的存储空间来存放临时数据。因此,它的空间复杂度是 O(1)。

四、完整示例

cpp 复制代码
#include <iostream>
using namespace std;

void bubbleSort(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {  // 外循环控制轮数
        for (int j = 0; j < n - i - 1; j++) {  // 内循环进行相邻元素的比较
            if (arr[j] > arr[j + 1]) {
                // 交换相邻元素
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
            }
        }
    }
}

int main() {
    int arr[] = {64, 25, 12, 22, 11};
    int n = sizeof(arr) / sizeof(arr[0]);

    bubbleSort(arr, n);
    
    cout << "Sorted array: ";
    for (int i = 0; i < n; i++) {
        cout << arr[i] << " ";
    }
    cout << endl;

    return 0;
}

优化:

标准的冒泡排序会在每一轮都进行完整的比较,但实际上,如果某一轮没有发生任何交换,那么可以认为数组已经排序好,排序可以提前终止。这样的优化会让最好的情况变成 O(n) 时间复杂度。

cpp 复制代码
void bubbleSortOptimized(int arr[], int n) {
    for (int i = 0; i < n - 1; i++) {
        bool swapped = false;  // 用于检查是否发生了交换
        for (int j = 0; j < n - i - 1; j++) {
            if (arr[j] > arr[j + 1]) {
                int temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swapped = true;
            }
        }
        if (!swapped) {
            break;  // 如果没有交换,提前退出循环
        }
    }
}
相关推荐
TechNomad3 小时前
排序算法:插入排序法
排序算法
不想写笔记1 天前
算法 C语言 冒泡排序
c语言·笔记·算法·排序算法
Han.miracle2 天前
数据结构与算法--007三数之和(medium)
算法·leetcode·排序算法
又是忙碌的一天2 天前
八大排序之:冒泡排序、快速排序和堆排序
数据结构·算法·排序算法
Lv11770082 天前
Visual Studio中的排序方法
数据结构·笔记·c#·排序算法·visual studio
喇一渡渡3 天前
Java力扣---滑动窗口(1)
java·算法·排序算法
ULTRA??3 天前
各种排序算法时间复杂度分析和实现和优势
c++·python·算法·排序算法
发疯幼稚鬼3 天前
归并排序与快速排序
c语言·数据结构·算法·排序算法
立志成为大牛的小牛4 天前
数据结构——六十、快速排序(王道408)
数据结构·程序人生·考研·算法·排序算法