要点
冒泡排序是一种交换排序。
什么是交换排序呢?
交换排序:两两比较待排序的关键字,并交换不满足次序要求的那对数,直到整个表都满足次序要求为止。
算法思想
它重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端,故名。
图-冒泡排序示例图
冒泡排序算法的执行步骤如下:(从后往前)
1、比较相邻的元素。如果第一个比第二个大,就交换他们两个。
2、对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。
3、针对所有的元素重复1、2的步骤,除了最后一个。
4、持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。
核心代码
public void bubbleSort(int[] list) {
int temp = 0; // 用来交换的临时数
// 要遍历的次数
for (int i = 0; i < list.length - 1; i++) {
// 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
for (int j = list.length - 1; j > i; j--) {
// 比较相邻的元素,如果前面的数大于后面的数,则交换
if (list[j - 1] > list[j]) {
temp = list[j - 1];
list[j - 1] = list[j];
list[j] = temp;
}
}
}
}
算法分析
冒泡排序算法的性能
排序类别 | 排序方法 | 时间复杂度 | 空间复杂度 | 稳定性 | 复杂性 | ||
平均情况 | 最坏情况 | 最好情况 | |||||
交换排序 | 冒泡排序 |
O(N2) |
O(N2) | O(Nlog2N) | O(1) | 稳定 | 简单 |
时间复杂度
若文件的初始状态是正序的,一趟扫描即可完成排序。所需的关键字比较次数C和记录移动次数M均达到最小值:Cmin = N - 1, Mmin = 0。所以,冒泡排序最好时间复杂度为O(N)。
若初始文件是反序的,需要进行 N -1 趟排序。每趟排序要进行 N - i 次关键字的比较(1 ≤ i ≤ N - 1),且每次比较都必须移动记录三次来达到交换记录位置。在这种情况下,比较和移动次数均达到最大值:
Cmax = N(N-1)/2 = O(N2)
Cmax = 3N(N-1)/2 = O(N2)
冒泡排序的最坏时间复杂度为O(N2)。
因此,冒泡排序的平均时间复杂度为O(N2)。
总结起来,其实就是一句话:当数据越接近正序时,冒泡排序性能约好。
算法稳定性
冒泡排序就是把小的元素往前调或者把大的元素往后调。比较是相邻的两个元素比较,交换也发生在这两个元素之间。
所以相同元素的前后顺序并没有改变,所以冒泡排序是一种稳定排序算法。
优化
对冒泡排序常见的改进方法是加入标志性变量exchange,用于标志某一趟排序过程中是否有数据交换。
如果进行某一趟排序时并没有进行数据交换,则说明数据已经按要求排列好,可立即结束排序,避免不必要的比较过程。
核心代码
public static void bubbleSort_2(int[] list) {
int temp = 0; // 用来交换的临时数
boolean bChange = false; // 交换标志
// 要遍历的次数
for (int i = 0; i < list.length - 1; i++) {
bChange = false;
// 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
for (int j = list.length - 1; j > i; j--) {
// 比较相邻的元素,如果前面的数大于后面的数,则交换
if (list[j - 1] > list[j]) {
temp = list[j - 1];
list[j - 1] = list[j];
list[j] = temp;
bChange = true;
}
}
// 如果标志为false,说明本轮遍历没有交换,已经是有序数列,可以结束排序
if (false == bChange)
break;
}
}
完整参考代码
JAVA版本
代码实现
1 import java.util.Random;
2
3 public class BubbleSort {
4
5 public void bubbleSort(int[] list) {
6 int temp = 0; // 用来交换的临时数
7
8 // 要遍历的次数
9 for (int i = 0; i < list.length - 1; i++) {
10 // 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
11 for (int j = list.length - 1; j > i; j--) {
12 // 比较相邻的元素,如果前面的数大于后面的数,则交换
13 if (list[j - 1] > list[j]) {
14 temp = list[j - 1];
15 list[j - 1] = list[j];
16 list[j] = temp;
17 }
18 }
19
20 System.out.format("第 %d 趟:\t", i);
21 printAll(list);
22 }
23 }
24
25 // 对 bubbleSort 的优化算法
26 public void bubbleSort_2(int[] list) {
27 int temp = 0; // 用来交换的临时数
28 boolean bChange = false; // 交换标志
29
30 // 要遍历的次数
31 for (int i = 0; i < list.length - 1; i++) {
32 bChange = false;
33 // 从后向前依次的比较相邻两个数的大小,遍历一次后,把数组中第i小的数放在第i个位置上
34 for (int j = list.length - 1; j > i; j--) {
35 // 比较相邻的元素,如果前面的数大于后面的数,则交换
36 if (list[j - 1] > list[j]) {
37 temp = list[j - 1];
38 list[j - 1] = list[j];
39 list[j] = temp;
40 bChange = true;
41 }
42 }
43
44 // 如果标志为false,说明本轮遍历没有交换,已经是有序数列,可以结束排序
45 if (false == bChange)
46 break;
47
48 System.out.format("第 %d 趟:\t", i);
49 printAll(list);
50 }
51 }
52
53 // 打印完整序列
54 public void printAll(int[] list) {
55 for (int value : list) {
56 System.out.print(value + "\t");
57 }
58 System.out.println();
59 }
60
61 public static void main(String[] args) {
62 // 初始化一个随机序列
63 final int MAX_SIZE = 10;
64 int[] array = new int[MAX_SIZE];
65 Random random = new Random();
66 for (int i = 0; i < MAX_SIZE; i++) {
67 array[i] = random.nextInt(MAX_SIZE);
68 }
69
70 // 调用冒泡排序方法
71 BubbleSort bubble = new BubbleSort();
72 System.out.print("排序前:\t");
73 bubble.printAll(array);
74 // bubble.bubbleSort(array);
75 bubble.bubbleSort_2(array);
76 System.out.print("排序后:\t");
77 bubble.printAll(array);
78 }
79 }
冒泡排序之JAVA代码实现
运行结果
排序前: 4 8 7 0 5 5 3 2 3 9
第 0 趟: 0 4 8 7 2 5 5 3 3 9
第 1 趟: 0 2 4 8 7 3 5 5 3 9
第 2 趟: 0 2 3 4 8 7 3 5 5 9
第 3 趟: 0 2 3 3 4 8 7 5 5 9
第 4 趟: 0 2 3 3 4 5 8 7 5 9
第 5 趟: 0 2 3 3 4 5 5 8 7 9
第 6 趟: 0 2 3 3 4 5 5 7 8 9
排序后: 0 2 3 3 4 5 5 7 8 9
参考资料
《数据结构习题与解析》(B级第3版)