冒泡排序的定义:
针对一个含有n个元素的数组,共进行n-1轮操作,每轮操作当前剩余元素的最大元素冒泡到数组末尾,每轮操作的具体过程为,对剩余元素从0开始两两比较并交换。
冒泡排序Java代码实现:
基础实现方式:
//冒泡排序基础版
private static void bubbleSort0() {
int[] arr = {1, 0, -3, 4, 6, -1, 8, 2, 9, 10};//共10个元素
System.out.println("排序前: " + Arrays.toString(arr));
for (int i = 1; i < arr.length; i++) {//共需要9轮操作,每轮操作确定一个当前最大元素,确定了9个即完成
for (int j = 0; j < arr.length - i; j++) {//从头开始,两两比较,确定剩余元素中最大元素
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
System.out.println("排序后: " + Arrays.toString(arr));
}
考虑到一种极端情况,给定的原始数组已经是有序数组了,如[-3, -1, 0, 1, 2, 4, 6, 8, 9, 10],在这种情况下,一轮操作都不需要,前面基础实现中,总共需要n-1轮的操作,考虑到有可能进行了小于n-1轮的操作后,数组已经有序了,那么n-1轮中剩余轮操作就不需要了,代码优化如下:
//冒泡排序优化一:轮数优化,针对进行到第n轮的时候已经是有序数组了,剩余轮数就没有必要比较了
private static void bubbleSort1() {
int[] arr = {-3, -1, 0, 1, 2, 4, 6, 8, 9, 10};//极端情况,给定的数组已经有序
System.out.println("排序前: " + Arrays.toString(arr));
for (int i = 1; i < arr.length; i++) {
boolean isSorted = true;
for (int j = 0; j < arr.length - i; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
isSorted = false;
}
}
if (isSorted) {
break;
}
}
System.out.println("排序后: " + Arrays.toString(arr));
}
前面的优化,针对的是轮数优化,说的是基础版本中总共需要n-1轮的操作,可能进行到比如说第3轮,第4轮,也就是进行到小于n-1轮的时候,数组已经有序了,那么n-1轮中剩余的轮数就不用操作了,另外一方面,还可以从每轮操作的范围进行优化,也即从无序边界的维度进行优化,基础版本中,无序边界 = n - 已确定元素,实际上无序边界可能比这种小,如数组[1, 0, -6, 3, 4, 5, 7, 8, 9, 10],实际上0,-6比较交换后,后面的元素已经是有序的了,无序再两两比较了,这种情况下代码优化如下:
//冒泡排序优化二:无序边界界定,即每轮操作可能并不需要对 n-已确定元素 个元素进行两两比较
public static void bubbleSort2() {
int[] arr = {1, 0, -6, 3, 4, 5, 7, 8, 9, 10};
int sortBorderPos = arr.length - 1;
int lastExchangePos = 0;
System.out.println("排序前: " + Arrays.toString(arr));
for (int i = 1; i < arr.length; i++) {
boolean isSorted = true;
for (int j = 0; j < sortBorderPos; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
isSorted = false;
lastExchangePos = j;
}
}
sortBorderPos = lastExchangePos;
if (isSorted) {
break;
}
}
System.out.println("排序后: " + Arrays.toString(arr));
}
实际上这两种优化的本质是去除无效操作,并不能根本上减小冒泡排序的时间复杂度。说到时间复杂度,冒泡排序的时间复杂度如下:
最好时间复杂度:O(n),即给定数组已经是有序数组的情况下,只需要进行一轮操作,n个元素两两比较一轮即可,参考优化一。
最坏时间复杂度:O(nn),即给定数组为完全逆序数组,这种情况下,共需要进行n-1轮操作,每轮操作对剩余元素都需要两两比较并交换元素。
平均时间复杂度:O(nn)