排序算法:冒泡排序法

一、冒泡排序简介

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

关键点:

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

冒泡排序的步骤:

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

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

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

初始状态:

[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]

最终排序结果:

[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)。

四、完整示例

#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) 时间复杂度。

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;  // 如果没有交换,提前退出循环
        }
    }
}
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值