定义
烈日炎炎的夏天,没有什么比一杯冰镇汽水更能让人平静了。
小气泡从杯底慢慢浮到表面的过程,就像冒泡排序的原理一样。
把每个元素当做一个气泡,每个元素与相邻的元素两两比较,根据大小来交换位置,一点点往数组的一端移动,最终得到一个有序的集合。
栗子
现在有 6 个数字组成的无序数列:
- 第一步:比较 6 和 4,发现 6 比 4 大,所以 6 和 4 交换位置。
- 第二步:比较 6 和 7,发现 6 比 7 小,所以位置不变。
- 第三步:比较 7 和 2,发现 7 比 2 大,所以 7 和 2 交换位置。
- 第四步:比较 7 和 8,发现 7 比 8 小,所以位置不变。
- 第五步:比较 8 和 5,发现 8 比 5 大,所以 8 和 5 交换位置。
这时候,冒泡排序的第一轮就完成了,8 作为最大的元素,就像汽水里的气泡一样,浮到了最右侧。
下面,我们来进行第二轮排序: - 第一步:比较 4 和 6,发现 4 比 6 小,所以位置不变。
- 第二步:比较 6 和 2,发现 6 比 2 大,所以 6 和 2 交换位置。
- 第三步:比较 6 和 7,发现 6 比 7 小,所以位置不变。
- 第四步:比较 7 和 5,发现 7 比 5 大,所以 7 和 5 交换位置。
- 第五步:比较 7 和 8,发现 7 比 8 小,所以位置不变。
第二轮排序结束后,数列右侧的有序区已经有了两个元素:7、8。
第三轮排序结束后:
第四轮排序结束后(已经是有序的了,所以顺序没有变化):
第五轮排序结束后(已经是有序的了,所以顺序没有变化):
第六轮排序结束后(已经是有序的了,所以顺序没有变化):
冒泡算法的每一轮排序,都要遍历所有元素,所以时间复杂度是 O(n²)。
实现
原始版
public static void sort(int[] array) {
int temp = 0;
for (int i = 0; i < array.length; i++) {
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
}
}
}
}
public static void main(String[] args) {
int[] array = new int[] { 6, 4, 7, 2, 8, 5 };
sort(array);
// 结果:[2, 4, 5, 6, 7, 8]
System.out.println(Arrays.toString(array));
}
Q:从上边的栗子来看,第三轮排序结束后,已经得到了有序的数列,但按照原始版的实现方式,还需要再进行 3 轮排序,怎样优化呢?
A:我们可以在判断出数列已经有序时,做出标记,让程序提前结束工作。
升级版
public static void sort(int[] array) {
int temp = 0;
for (int i = 0; i < array.length; i++) {
// 每轮排序前,假设已经是有序的了,有序标记都是 true
boolean isSorted = true;
for (int j = i + 1; j < array.length; j++) {
if (array[i] > array[j]) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
// 如果需要调整,说明还不是有序数列,标记为 false
isSorted = false;
}
}
// 如果数列已经有序,不再进行下一轮排序
if (isSorted) {
break;
}
}
}
public static void main(String[] args) {
int[] array = new int[] { 6, 4, 7, 2, 8, 5 };
sort(array);
// 结果:[2, 4, 5, 6, 7, 8]
System.out.println(Arrays.toString(array));
}
Q:现在的冒泡排序实现方式,有序区域的长度跟排序轮数是一致的,但如果数列本来就存在有序的部分,在前几轮重复比较有序的元素是没有意义的,怎样优化呢?
A:我们可以在每一轮排序结束时,记录下最后一次交换元素的位置,这个位置就是无序数列的边界,相反区域就是有序数列了。
最终版
public static void sort(int[] array) {
int temp = 0;
// 最后一次交换的位置
int lastExchange = 0;
for (int i = 0; i < array.length; i++) {
// 每轮排序前,假设已经是有序的了,有序标记都是 true
boolean isSorted = true;
for (int j = lastExchange; j < array.length; j++) {
if (array[i] > array[j]) {
temp = array[i];
array[i] = array[j];
array[j] = temp;
// 如果需要调整,说明还不是有序数列,标记为 false
isSorted = false;
// 记录最后一次交换的位置
lastExchange = j;
}
}
// 如果数列已经有序,不再进行下一轮排序
if (isSorted) {
break;
}
}
}
public static void main(String[] args) {
int[] array = new int[] { 6, 4, 7, 2, 8, 5 };
sort(array);
// 结果:[2, 4, 5, 6, 7, 8]
System.out.println(Arrays.toString(array));
}