算法分析:什么是冒泡排序?

2011年,C语言老师给我们讲冒泡排序。

2014年,听同学说起他面试时和面试官提起冒泡排序。

2015年,大学本科刚毕业,开始找工作,我还记得我在笔记中写了冒泡排序的代码,结果还真的被问到了。

2019年,公司首席架构师跟我提到冒泡排序。

什么是冒泡排序呢?

冒泡排序,Bubble Sort,通过依次来比较相邻两个元素的大小,在每一次的比较的过程中,两个元素,通过交换来达到有序的目的。

  • 如果一组无序的数列想要从小到大排序,那么两个元素比较,通过交换来实现,左边的元素要比右边的元素要小。
  • 如果一组无序的数列想要从大到小排序,那么两个元素比较,通过交换来实现,左边的元素要比右边的元素要大。

就像碳酸饮料中的气泡一样,从底部一直冒泡到顶部。

我想通过一组数据来阐述冒泡排序的过程。

  • 准备一组无序的数列,从小到大依次排序。

  • 开始排序开始。由于6>3,因此两个元素交换。

 

  • 由于6>2,因此两个元素交换。

  • 由于6>1,因此两个元素交换。

  • 由于6<8,因此两个元素不交换。
  • 由于8<9,因此两个元素不交换。
  • 由于9>7,因此两个元素交换。

  • 由于9>5,因此两个元素交换。

  • 第一轮排序结束。此时,元素9处于有序区域。

  • 第二轮排序开始。由于3>2。因此两个元素交换。

  • 由于3>1。因此两个元素交换。

  • 由于3<6。因此两个元素不交换。
  • 由于6<8。因此两个元素不交换。
  • 由于8>7。因此两个元素交换。

  • 由于8>5。因此两个元素交换。

  • 由于8<9。因此两个元素不交换。
  • 第二轮排序结束。此时,元素8和9处于有序区域。

  • 第三轮排序开始。由于2>1。因此两个元素交换。

 

  • 由于2<3。因此两个元素不交换。
  • 由于3<6。因此两个元素不交换。
  • 由于6<7。因此两个元素不交换。
  • 由于7>5,因此两个元素交换。

  • 第三轮排序结束此时,元素7,8和9处于有序区域。

  • 第四轮排序开始。由于1<2。因此两个元素不交换。
  • 由于2<3。因此两个元素不交换。
  • 由于3<6。因此两个元素不交换。
  • 由于6>5。因此两个元素交换。

  • 第四轮排序结束。此时,元素6,7,8,9在有序区域内。

 

  • 第五轮排序开始。由于1<2。因此两个元素不交换。
  • 由于2<3。因此两个元素不交换。
  • 由于3<5。因此两个元素不交换。
  • 第五轮排序结束。此时,元素5,6,7,8,9在有序区域内。
  • 第六轮排序开始。由于1<2。因此两个元素不交换。
  • 由于2<3。因此两个元素不交换。
  • 轮排序结束。此时,元素3,5,6,7,8,9在有序区域内。
  • 第七轮排序开始。由于1<2。因此两个元素不交换。
  • 轮排序结束。此时,元素2,3,5,6,7,8,9在有序区域内。
  • 第八轮排序开始
  • 轮排序结束。此时,元素1,2,3,5,6,7,8,9在有序区域内。可见最后一轮没有必要存在。

最后我们使用Java代码来展示上述的算法。

 1 private static void sort() {
 2 
 3         Integer[] data = {6,3,2,1,8,9,7,5};
 4 
 5         for(int i=0; i<data.length-1; i++) {
 6 
 7             for(int j=0; j<data.length-i-1; j++) {
 8 
 9                 if(data[j] > data[j+1]) {
10                     int k = data[j];
11                     data[j] = data[j+1];
12                     data[j+1] = k;
13                 }
14 
15             }
16         }
17 
18     }

从上述的代码以及流程图中。大致的步骤如下:

  1. 定义一组无序数列。
  2. 第一层循环。第一层循环的次数是(元素个数-1)
  3. 第二层循环。第二层循环的次数是无序区域内,元素1和元素2,元素2和元素3...依次两两比较。
  4. 两个元素比较,根据规则确认元素是否需要交换。
  5. 继续第二步。直到循环的次数 = (元素个数-1),结束循环,输出有序数列,结束。

从以上的现象来看,我们得出结论和思考:

  • 排序的轮数等于元素的个数-1。是否可以较少轮数,上述的例子中实际上在完成第四轮以后已经是一组有序的数列。从上述代码来看,第5行控制的是轮数,而第7行是每一轮中无序区域中元素的两两比较。从本质上来说,我们需要在发现没有元素交换的情况下,就能够说明已经有序,我们需要尽早在21行结束。代码如下所示:
 1 private static void sort() {
 2 
 3         Integer[] data = {6,3,2,1,8,9,7,5};
 4 
 5         for(int i=0; i<data.length-1; i++) {
 6 
 7             boolean isSorted = true;
 8 
 9             for(int j=0; j<data.length-i-1; j++) {
10 
11                 if(data[j] > data[j+1]) {
12                     int k = data[j];
13                     data[j] = data[j+1];
14                     data[j+1] = k;
15 
16                     isSorted = false;
17                 }
18             }
19 
20             if(isSorted) {
21                 break;
22             }
23         }
24 
25

从上述的代码和流程图中,如果在第一层的循环中第二次始终没有元素交换,表明数列已经有序,直接输出

  • 完成每一轮,有序区域的元素个数增加1。是否可以冲破每一次有序区域只能增加1的僵局?我们采取在一个大的轮询中,记录最后交换的元素的下标,把这个下标作为无序区域的边界,用来最大提高有序区域元素数量增加的加速度。
 1 private static void sort() {
 2 
 3         Integer[] data = {6,3,2,1,8,9,7,5};
 4 
 5         //最后交换的元素的下标
 6         int lastIndexOfSwap = 0;
 7         //无序数列边界元素的下标
 8         int borderIndexOfUnsort = data.length - 1;
 9 
10         for(int i=0; i<data.length-1; i++) {
11 
12             boolean isSorted = true;
13 
14             for(int j=0; j<borderIndexOfUnsort; j++) {
15 
16                 if(data[j] > data[j+1]) {
17                     int k = data[j];
18                     data[j] = data[j+1];
19                     data[j+1] = k;
20 
21                     isSorted = false;
22 
23                     lastIndexOfSwap = j;
24                 }
25             }
26 
27             borderIndexOfUnsort = lastIndexOfSwap;
28 
29             if(isSorted) {
30                 break;
31             }
32 
33         }
34 
35     }

冒泡排序是最简单的一种排序算法,3个版本演进,也许并没有提高多大的效能,更多应该是对于算法的思维。

原文地址:https://www.cnblogs.com/behindyou/p/10567041.html