数据结构与算法——冒泡排序

本专栏是学习王争老师的《数据结构与算法之美》的学习总结,详细内容可以去学习王争老师的专栏,希望大家都能够有所收获。同时也欢迎大家能够与我一起交流探讨!

冒泡排序(Bubble Sort)

1、冒泡排序介绍

冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。

举个例子,展示冒泡排序的整个过程。对一组数据 4,5,6,3,2,1,从小到大进行排序。第一次冒泡操作的详细过程就是这样:
在这里插入图片描述
从上图可以看出,经过一个冒泡操作后,6这个元素已经存储在正确的位置上。想要完成所有数据的排序,只需要进行6次这样的冒泡操作就行了。
在这里插入图片描述

实际上,冒泡过程还可以优化。当某次冒泡操作已经没有数据交换时,说明已经达到完全有序,不用再继续执行后续的冒泡操作

举个例子,如下6个元素排序,只需要4次冒泡排序操作就可以了。
在这里插入图片描述
冒泡排序的代码如下:

// 冒泡排序,arr表示数组,n表示数组大小
public void bubbleSort(int[] arr, int n) {
  if (n <= 1) return;
 
 for (int i = 0; i < n; i++) {
    // 提前退出冒泡循环的标志位
    boolean flag = false;
    for (int j = 0; j < n - i - 1; j++) {
      if (arr[j] > arr[j+1]) { // 交换
        int tmp = arr[j];
        arr[j] = arr[j+1];
        arr[j+1] = tmp;
        flag = true;  // 表示有进行数据交换      
      }
    }
    if (!flag) break;  // 没有数据交换,提前退出
  }
}

结合刚才的排序算法三方面分析。

2、冒泡排序是原地排序算法吗?

冒泡的过程只涉及相邻数据的交换操作,只需要常量级的临时空间,所以它的空间复杂度为 O(1),是一个原地排序算法。

3、冒泡排序是稳定的排序算法吗?

在冒泡排序中,只有交换才可以改变两个元素的前后顺序。为了保证冒泡排序算法的稳定性,当有相邻的两个元素大小相等的时候,不做交换,相同大小的数据在排序前后不会改变顺序,所以冒泡排序是稳定的排序算法

4、冒泡排序的时间复杂度是多少?

最好情况下,要排序的数据已经是有序的了,只需要进行一次冒泡操作就可以结束,所以最好情况时间复杂度是 O(n)。

最坏的情况是,要排序的数据刚好是倒序排列的,需要进行 n 次冒泡操作,所以最坏情况时间复杂度为 O(n^2)。

在这里插入图片描述
冒泡排序的平均时间复杂度,即加权平均期望时间复杂度。通过**“有序度”“逆序度”**这两个概念来进行分析。

有序度是数组中具有有序关系的元素对的个数。有序元素对用数学表达式表示就是这样:

有序元素对:a[i] <= a[j], 如果i < j。

在这里插入图片描述
同理,对于一个倒序排列的数组,比如 6,5,4,3,2,1,有序度是 0;对于一个完全有序的数组,比如 1,2,3,4,5,6,有序度就是 n(n-1)/2*,也就是 15。我们把这种完全有序的数组的有序度叫作满有序度

逆序度的定义正好跟有序度相反(默认从小到大为有序)。

逆序元素对:a[i] > a[j], 如果i < j。

关于这三个概念,还可以得到一个公式:逆序度 = 满有序度 - 有序度。排序的过程就是一种增加有序度,减少逆序度的过程,最后达到满有序度,就说明排序完成了。

举个例子,要排序的数组初始状态为4,5,6,3,2,1 ,其中,有序元素对有 (4,5) (4,6)(5,6),所以有序度是 3。n=6,所以排序完成之后终态的满有序度为 n*(n-1)/2=15。
在这里插入图片描述
冒泡排序包含两个操作原子,比较交换。每交换一次,有序度就加 1。不管算法怎么改进,交换次数总是确定的,即为逆序度,也就是n*(n-1)/2–初始有序度。此例中就是 15–3=12,要进行 12 次交换操作。

对于包含 n 个数据的数组进行冒泡排序,平均交换次数是多少呢?

  • 最坏情况下,初始状态的有序度是 0,所以要进行 n*(n-1)/2 次交换。
  • 最好情况下,初始状态的有序度是 n*(n-1)/2,就不需要进行交换。
  • 可以取个中间值 n*(n-1)/4,来表示初始有序度既不是很高也不是很低的平均情况。

平均情况下,需要 n*(n-1)/4 次交换操作,比较操作肯定要比交换操作多,而复杂度的上限是 O(n^2),所以平均情况下的时间复杂度就是 O(n ^2)。

这个平均时间复杂度推导过程其实并不严格,但是很多时候很实用,毕竟概率论的定量分析太复杂,不太好用。

总结

1、冒泡排序是一种只会操作相邻两个数据的算法,每次冒泡操作都会对相邻的两个元素进行比较,看是否满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在的位置,重复 n 次,就完成了 n 个数据的排序工作。

2、冒泡排序算法只涉及相邻数据的交换操作,只需要常量级的临时空间,空间复杂度为 O(1),是一个原地排序算法

3、在冒泡排序中,只有交换才可以改变两个元素的前后顺序。冒泡排序算法在设计上,当有相邻的两个元素大小相等的时候两个元素不进行交换,所以冒泡排序是稳定的排序算法。

4、最好情况下,要排序的数据已经是有序,只需要进行一次冒泡操作就可以结束操作,最好情况时间复杂度是 O(n)。

5、最坏情况下,要排序的数据刚好是倒序排列的,需要进行 n 次冒泡操作,最坏情况时间复杂度为 O(n^2)。

6、通过有序度、逆序度、满有序度的概念,得到公式:逆序度 = 满有序度 - 有序度,其中满有序度一般为n*(n-1)/2,其中n为数组的大小。

冒泡排序包含两个操作原子,比较交换每交换一次,有序度就加 1逆序度减1

整个冒泡算法的交换次数即为逆序度,即交换次数 == 逆序度n*(n-1)/2 – 初始有序度。对于包含 n 个数据的数组进行冒泡排序:

  • 最坏情况下,初始状态的有序度是0,所以要进行 n*(n-1)/2 次交换。
  • 最好情况下,初始状态的有序度是 n*(n-1)/2,就不需要进行交换。
  • 取个中间值n*(n-1)/4来表示初始有序度既不是很高也不是很低的平均情况。在冒泡算法中,比较操作肯定比交换操作多,而复杂度的上限是 O(n^2),所以平均情况下的时间复杂度就是 O(n ^2)。
  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值