冒泡排序 (Bubble Sort)
冒泡排序 (Bubble Sort)
冒泡排序的基本概念
冒泡排序(Bubble Sort)也是一种简单直观的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢"浮"到数列的顶端。
作为最简单的排序算法之一,冒泡排序给我的感觉就像 Abandon 在单词书里出现的感觉一样,每次都在第一页第一位,所以最熟悉。冒泡排序还有一种优化算法,就是立一个 flag,当在一趟序列遍历中元素没有发生交换,则证明该序列已经有序。但这种改进对于提升性能来说并没有什么太大作用。
冒泡排序的算法步骤
比较相邻的元素。如果第一个比第二个大,就交换他们两个。
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。
针对所有的元素重复以上的步骤,除了最后一个。
持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
给定一个N个元素的数组,冒泡法排序将:
- 如果元素大小关系不正确,交换这两个数(在本例中为a> b),
- 比较一对相邻元素(a,b),
- 重复步骤1和2,直到我们到达数组的末尾(最后一对是第(N-2)和(N-1)项,因为我们的数组从零开始)
- 到目前为止,最大的元素将在最后的位置。 然后我们将N减少1,并重复步骤1,直到N = 1。
冒泡排序的分析
冒泡排序的算法时间分析
比较和交换需要一个以常量为界的时间,我们称之为c。
(标准)Bubble Sort中有两个嵌套循环。
外循环正好运行N次迭代。 但内部循环运行变得越来越短:
- 当 i = 0,(N-1)次迭代(比较和可能交换)时。
- 当 i = 1,(N-2)次迭代时,...
- 当 i =(N-2)时,1次迭代,
- 当 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]