1.什么冒泡排序
冒泡排序是通过相邻2个元素比较,依次找到当次循环最大值放到右边,通过当前数组(length-1)次外围循环比较,得出最终的有序数组。
2.冒泡排序常见方式
- 下面普通的冒泡排序方式:通过内层冒泡比较当前最大值和(length-1)比较次数
/**
* 冒泡排序,相邻比较,最大的右移
* 常见版本
*
* @param arr
*/
public static void bubbleSort_1(int[] arr) {
//比较次数length-1次
for (int i = 0; i < arr.length - 1; i++) {
//单词冒泡:比较相邻2个数值大小,大的右移动
for (int j = 0; j < arr.length - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
System.out.printf("bobbleSort_1 ==> arr:" + Arrays.toString(arr) + "\n\r");
}
}
打印输出
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 4, 5, 7, 3, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 4, 5, 3, 7, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 4, 3, 5, 7, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
bobbleSort_1 ==> 冒泡内循环比较次数:0
bobbleSort_1 ==> 冒泡内循环比较次数:1
bobbleSort_1 ==> 冒泡内循环比较次数:2
bobbleSort_1 ==> 冒泡内循环比较次数:3
bobbleSort_1 ==> 冒泡内循环比较次数:4
bobbleSort_1 ==> 冒泡内循环比较次数:5
bobbleSort_1 ==> 冒泡内循环比较次数:6
bobbleSort_1 ==> 冒泡内循环比较次数:7
bobbleSort_1 ==> arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
可以看出来不管数组是否已经排序完成,都需要循环(length-1)次,比较浪费时间。
- 根据上面方式优化的版本:优化外层循环的次数,当其中内循环未交换元素,说明当前数据已经是有序的
/**
* 冒泡排序,相邻比较,最大的右移
* 优化版本-当其中一次没有比较交换时,说明已经完成排序了
*
* @param arr
*/
public static void bubbleSort_2(int[] arr) {
//比较次数length-1次
for (int i = 0; i < arr.length - 1; i++) {
Boolean isSwap = false; //是否发生比较交换元素
//每次右移找到最大值,所以比较次数相对应该减少i
for (int j = 0; j < arr.length - 1 - i; j++) {
System.out.printf("bubbleSort_2 ==> 冒泡内循环比较次数:" + j + "\n\r");
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
isSwap = true;
}
}
System.out.printf("bubbleSort_2 ==>第" + i + "次比较, arr:" + Arrays.toString(arr) + "\n\r");
//当其中一次没有比较交换时,说明已经完成排序了
if (!isSwap) {
break;
}
}
}
打印输出
bubbleSort_2 ==> 冒泡内循环比较次数:0
bubbleSort_2 ==> 冒泡内循环比较次数:1
bubbleSort_2 ==> 冒泡内循环比较次数:2
bubbleSort_2 ==> 冒泡内循环比较次数:3
bubbleSort_2 ==> 冒泡内循环比较次数:4
bubbleSort_2 ==> 冒泡内循环比较次数:5
bubbleSort_2 ==> 冒泡内循环比较次数:6
bubbleSort_2 ==> 冒泡内循环比较次数:7
bubbleSort_2 ==>第0次比较, arr:[1, 2, 4, 5, 7, 3, 9, 10, 21]
bubbleSort_2 ==> 冒泡内循环比较次数:0
bubbleSort_2 ==> 冒泡内循环比较次数:1
bubbleSort_2 ==> 冒泡内循环比较次数:2
bubbleSort_2 ==> 冒泡内循环比较次数:3
bubbleSort_2 ==> 冒泡内循环比较次数:4
bubbleSort_2 ==> 冒泡内循环比较次数:5
bubbleSort_2 ==> 冒泡内循环比较次数:6
bubbleSort_2 ==>第1次比较, arr:[1, 2, 4, 5, 3, 7, 9, 10, 21]
bubbleSort_2 ==> 冒泡内循环比较次数:0
bubbleSort_2 ==> 冒泡内循环比较次数:1
bubbleSort_2 ==> 冒泡内循环比较次数:2
bubbleSort_2 ==> 冒泡内循环比较次数:3
bubbleSort_2 ==> 冒泡内循环比较次数:4
bubbleSort_2 ==> 冒泡内循环比较次数:5
bubbleSort_2 ==>第2次比较, arr:[1, 2, 4, 3, 5, 7, 9, 10, 21]
bubbleSort_2 ==> 冒泡内循环比较次数:0
bubbleSort_2 ==> 冒泡内循环比较次数:1
bubbleSort_2 ==> 冒泡内循环比较次数:2
bubbleSort_2 ==> 冒泡内循环比较次数:3
bubbleSort_2 ==> 冒泡内循环比较次数:4
bubbleSort_2 ==>第3次比较, arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
bubbleSort_2 ==> 冒泡内循环比较次数:0
bubbleSort_2 ==> 冒泡内循环比较次数:1
bubbleSort_2 ==> 冒泡内循环比较次数:2
bubbleSort_2 ==> 冒泡内循环比较次数:3
bubbleSort_2 ==>第4次比较, arr:[1, 2, 3, 4, 5, 7, 9, 10, 21]
从日志中可以看出,明显的外层循环次数少了,因为在第5次比较完成后,数组已经是有序得了,第六为了进行比对交换,所以直接bredk。
- 最终优化方案:1.减少内层比较次数,最后一次交换位置和后面已经完成排序,后续不需要在进行比较交换
/**
* 冒泡排序,相邻比较,最大的右移
* 最终优化版本-1.减少内层比较次数,最后一次交换位置和后面已经完成排序,后续不需要在进行比较交换
*
* @param arr
*/
public static void bubbleSort_3(int[] arr) {
//比较次数
int n = arr.length - 1;
while (true) {
int last = 0; //最后一次交换的位置
//每次右移找到最大值,记录最后一次交换的左边位置,表示 j~length 已经完成排序
for (int j = 0; j < n; j++) {
System.out.printf("bubbleSort_3 ==> 冒泡内循环比较次数:" + j + "\n\r");
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
//如果最后一次交换的是index =0,则标识已经完成排序
last = j;
}
}
n = last;
System.out.printf("bubbleSort_3 ==>比较, arr:" + Arrays.toString(arr) + "\n\r");
//如果最后一次交换的是index =0,则标识已经完成排序
if (n == 0) {
break;
}
}
}
每次的比对最后一次的比对位置和其后面的last位置元素都已经按照顺序排列,只需要进行 index < last位置的元素比对交换,直到最后一次比对位置last==0,说明已经是最小元素,跳出循环,由于直接使用last来较少循环次数,没有使用到外层训循环,直接可以用while(true)替换,更加美观方便。