冒泡排序 (Bubble Sort)

冒泡排序 (Bubble Sort)

冒泡排序的基本概念

冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。

作为最简单的排序算法之一,冒泡排序给我的感觉就像 Abandon 在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉。冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但这种改进对于提升性能来说并没有什么太大作用

冒泡排序的算法步骤

比较相邻的元素。如果第一个比第二个大,就交换他们两个。

对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。

针对所有的元素重复以上的步骤,除了最后一个。

持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。

给定一个N个元素的数组,冒泡法排序将:

  1. 如果元素大小关系不正确,交换这两个数(在本例中为a> b),
  2. 比较一对相邻元素(a,b),
  3. 重复步骤1和2,直到我们到达数组的末尾(最后一对是第(N-2)和(N-1)项,因为我们的数组从零开始)
  4. 到目前为止,最大的元素将在最后的位置。 然后我们将N减少1,并重复步骤1,直到N = 1。

冒泡排序的分析

冒泡排序的算法时间分析

比较和交换需要一个以常量为界的时间,我们称之为c。
(标准)Bubble Sort中有两个嵌套循环。
外循环正好运行N次迭代。 但内部循环运行变得越来越短:

  1. 当 i = 0,(N-1)次迭代(比较和可能交换)时。
  2. 当 i = 1,(N-2)次迭代时,...
  3. 当 i =(N-2)时,1次迭代,
  4. 当 i=(N-1),0迭代.

因此,总迭代次数=(N-1)+(N-2)+ ... + 1 + 0 = N *(N-1)/ 2。
总时间= c * N *(N-1)/ 2 = O(N ^ 2)。
冒泡排序实际上是低效的,它的 O(N^2) 时间复杂度。 想象一下,我们有 N = 106 个数字。 即使我们的计算机速度超快,并且可以在1秒内计算108次操作,但冒泡排序仍需要大约100秒才能完成。
但是,它可以提前终止。
冒泡排序什么时候最快:
当输入的数据已经是正序时(都已经是正序了,我还要你冒泡排序有何用啊)。
冒泡排序什么时候最慢:
当输入的数据是反序时(写一个 for 循环反序输出数据不就行了,干嘛要用你冒泡排序呢,我是闲的吗)。

冒泡排序的实例分析

以数组 arr = [5, 1, 4, 2, 8] 为例说明,加粗的数字表示每次循环要比较的两个数字:

第一次外循环

( 5 1 4 2 8 ) → ( 1 5 4 2 8 ), 5 > 1 交换位置
( 1 5 4 2 8 ) → ( 1 4 5 2 8 ), 5 > 4 交换位置
( 1 4 5 2 8 ) → ( 1 4 2 5 8 ), 5 > 2 交换位置
( 1 4 2 5 8 ) → ( 1 4 2 5 8 ), 5 < 8 位置不变

第二次外循环(除开最后一个元素8,对剩余的序列)

( 1 4 2 5 8 ) → ( 1 4 2 5 8 ), 1 < 4 位置不变
( 1 4 2 5 8 ) → ( 1 2 4 5 8 ), 4 > 2 交换位置
( 1 2 4 5 8 ) → ( 1 2 4 5 8 ), 4 < 5 位置不变

第三次外循环(除开已经排序好的最后两个元素,可以注意到上面的数组其实已经排序完成,但是程序本身并不知道,所以还要进行后续的循环,直到剩余的序列为 1)

( 1 2 4 5 8 ) → ( 1 2 4 5 8 )
( 1 2 4 5 8 ) → ( 1 2 4 5 8 )

第四次外循环(最后一次)
( 1 2 4 5 8 ) → ( 1 2 4 5 8 )

冒泡排序的动图演示

冒泡排序的代码实现

public static void main(String[] args) {
    // 定义数组
    int[] a = new int[10];
    // 数据初始化 (0~99) 随机数
    for (int i = 0; i < 10; i++) {
        a[i] = new Random().nextInt(99);
    }

    // 排序前输出
    System.out.println("排序前: " + Arrays.toString(a));
    // 排序
    for (int i = 0; i < a.length - 1; i++) {
        // 这里判定条件 - i 是为了提升性能, 即已经排序的无序再进行比较
        for (int j = 0; j < a.length - 1 - i; j++) {
            // 这里大于号为升序, 小于号为降序
            if (a[j] > a[j + 1]) {
                int t = a[j];
                a[j] = a[j + 1];
                a[j + 1] = t;
            }
        }
    }
    // 排序后输出
    System.out.println("排序后: " + Arrays.toString(a));
}

结果:

排序前: [42, 41, 35, 84, 7, 81, 98, 88, 48, 11]
排序后: [7, 11, 35, 41, 42, 48, 81, 84, 88, 98]
posted @ 2020-07-16 17:29  zpk-aaron  阅读(605)  评论(0编辑  收藏  举报