数据结构和算法:冒泡排序

211 阅读4分钟

冒泡排序

简介

冒泡排序(Bubble Sort)

它的工作方式就像是 可乐中的气泡,越轻的气泡就会慢慢的浮到上面,在排序中,越小的元素经过交换也会慢慢的浮现在数列的顶端。

它是最基础的排序算法 优点:

  • 便于学习,是最容易了解和实现的排序算法之一

缺点

  • 它对于大量元素的排序 是非常没有效率的

实例

让我们来看看具体的案例:

此时此刻存在一个无序的数组,我们希望它能从小到大的排序。

根据冒泡排序的算法思想:一次比较两个相邻的元素,如果顺序错误就将他们交换位置

那么对于上面的无序数组,我们应该先去比较 2910 ,此时我们发现 29 > 10 ,那么我们就需要去将它们交换位置。

2910 交换位置之后,继续将 2914 比较,可以发现 29 > 14 , 那么继续交换位置。

1429 交换位置之后,继续将 2937 比较,此时 29 < 37 , 这样就不需要交换位置。

然后继续将 3714 比较,存在 37 > 14 , 那么需要交换元素之间的位置。

这样第一轮的排序就完成啦,可乐中最小的气泡就冒出头了。

我们可以将左侧看作无序的,而右侧看作有序的,目前有序的地方只有一个 37

OK,让我们来进行第二轮的排序。

1014 比较, 因为 14 > 10 ,所以元素位置不会发生改变。

继续将 1429 进行比较,由于 29 > 14 , 所以元素位置不发生变化。

继续进行比较, 这次 14 < 29 需要将元素的位置发生变化。

这个时候,不需要再将 2937 进行比较了,那么有序组有有了两个成员了。

后面同理之下, 第三轮:

第四轮:

第五轮:

这样数组的所有的元素都排列完成,成功从无序数组排列成为一个从小到大的有序数组。

代码

冒泡排序的Java代码:

public static void bubble_sort(int[] arr) {
  int temp;
  int len = arr.length;
    for (int i = 0; i < len - 1; i++) {
      for (int j = 0; j < len - 1 - i; j++) {
        if (arr[j] > arr[j + 1]) {
             temp = arr[j];
             arr[j] = arr[j + 1];
             arr[j + 1] = temp;
        }
      }
    }
}

可以看到代码其实是很简单的,通过一个外循环和一个内循环来进行元素比较, 外循环 = 轮数; 内循环 = 每一轮中元素的一次次比较。

每一次内循环只要比较无序组中的元素,所以条件中要减去有序组的长度也就是外循环的 i 的大小( j < len - 1 - i )。

代码的优化

对于冒泡排序的优化主要体现在减少比较次数。

如果存在一个 相对有序的数组,比如{ 2,1,3,4 },只需要第一轮就可得到一个有序的数组,但是冒泡排序还是会继续无意义的两两比较,这样就浪费了大量的时间。

那么问题来了,我们该怎么优化它呢?

如果在内循环中,元素没有发生一次交换,是不是说明所有元素都是按照顺序排列的。

所以我们可以设置一个标志 flag ,默认为 false,如果内循环中,元素发生了交换,那么可以将 flag 置为 true,在下轮外循环的时候,先检查 flag ,如果 flag 为 false,那么排序就结束了。

所以代码如下:

public static void bubble_sort(int[] arr) {

    boolean flag = true;
    int temp;
    int len = arr.length;
    for (int i = 0; i < len - 1 && flag; i++) {
         flag = false;
         for (int j = 0; j < len - 1 - i; j++) {
               if (arr[j] > arr[j + 1]) {
                   flag = true;
                   temp = arr[j];
                   arr[j] = arr[j + 1];
                   arr[j + 1] = temp;
                }
          }
    }
}

将优化前 和 优化后的代码 进行比较:

public static void main(String []args) {

int[] arrs = new int[10000];
for(int i = 0; i < 10000; i++) {
arrs[i] = (int) (Math.random() * (10000 + 1));
}

long time = System.currentTimeMillis();
bubble_sort1(arrs);
long time1 = System.currentTimeMillis();
System.out.println("bubble_sort1 time : " +(time1 - time));

bubble_sort2(arrs);
System.out.println("bubble_sort2 time : " +(System.currentTimeMillis() - time1));

}

从结果看来还是有些效果的:

时间复杂度

在设置标签后,

  • 最好的情况:正序的数组,时间复杂度就是O(n)
  • 最坏的情况:逆序的数组,时间复杂度是O(n^2)
  • 一般情况:杂乱无序的数组,平均时间复杂度就是O(n^2)

总结

冒泡排序是最简单的入门算法之一,算法是程序员需要掌握的基础,对我们的发展会有很大的帮助。