算法思想
冒泡排序是一种交换排序。
交换排序:两两比较待排序的关键字,把不满足次序要求的两个数交换,直到全部关键字都满足次序要求为止。
冒泡排序的算法思想:假设要对N个数进行排序,第一趟,先比较0位置和1位置的两个数,如果不满足要求就交换两个数,满足要求则不动,再比较1位置和2位置的数,以此类推,一直比较到最后两个数,此时,最后一个数就是最大(小)的。第二趟,从1位置的数开始两两比较。每一趟排除已经确定的数。将越来越少的数两两比较直到所有的数都完成排序。趟数为N-1。
例子:
例如:10 2 8 7
第一轮
(1)10>2,二者交换位置:2 10 8 7
(2)10>8,二者交换位置:2 8 10 7
(3)10>7,二者交换位置:2 8 7 10
这一轮结束,下一轮不再比较最后一个元素
第二轮
(1)2<8,不交换位置:2 8 7 10
(2)8>7.交换位置:2 7 8 10
结束这一轮,下一轮不再比较最后二个元素
第三轮
(1)2<7,不交换:2 7 8 10
排序结束。
Java代码实现
public static void bubblesort(int arr[]){
//为空,或长度小于2,不排序直接返回
if(arr==null || arr.length<2){
System.out.println("长度过短,不能进行排序");
return;
}
// 一共比较数组长度-1轮
for (int i = 0; i < arr.length - 1; i++) {
// 每一轮的比较次数,长度减一再减轮数(第一轮的时候,最大的已在最后,第二轮是就不用和最后一个比较了,依此类推)
for (int j = 0; j < arr.length - 1 - i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
private static void swap(int[] arr, int i, int j) {
int temp=0;
temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
或
public static void bubblesort2(int arr[]){
//为空,或长度小于2,不排序直接返回
if(arr==null || arr.length<2){
System.out.println("长度过短,不能进行排序");
return;
}
//外层循环,遍历次数为数组长度-1
for (int i = arr.length - 1; i > 0; i--) {
for (int j = 0; j < i; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr, j, j + 1);
}
}
}
}
用对数器对算法进行正确性测试
对数器的概念:
0:有一个你想要测的方法a
1:实现一个绝对正确但是复杂度不好的方法b
2:实现一个随机样本产生器
3:实现比对的方法
4:把方法a和方法b比对很多次来验证方法a是否正确
5:如果有一个样本使得比对出错,打印样本分析是哪个方法出错
6:当样本数量很多时比对测试依然正确,可以确定方法a已经正确
下面用代码一步步实现对数器
0:有一个你想要测的方法a
上面所实现的冒泡排序
1:实现一个绝对正确但是复杂度不好的方法b
这里使用系统的排序算法
// 绝对正确的方法
public static void rightMethod(int[] arr) {
Arrays.sort(arr);
}
2:实现一个随机样本产生器
//随机数组生成器
public static int[] generateRandomArray(int size, int value) {
/*
************java中的数字随机生成**********
Math.random() -> double [0,1)
(int)((size+1)*Math.random()) -> int [0,size]
例如size=5,size+1=6
[0,1) * 6 -> double [0,6) -> int [0,5]
*/
// 生成随机长度(0-size)
int[] arr = new int[(int) (Math.random() * (size + 1))];
// 生成随机数字(-value 到 value)
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) ((value + 1) * Math.random()) - (int)(value * Math.random());
}
return arr;
}
3:实现比对的方法
//判断两个数组是否相等
public static boolean isEqual(int[] a1, int[] a2) {
if ((a1 == null && a2 != null) || (a1 != null && a2 == null)) {
return false;
}
if (a1 == null && a2 == null) {
return true;
}
if (a1.length != a2.length) {
return false;
}
for (int i = 0; i < a1.length; i++) {
if (a1[i] != a2[i]) {
return false;
}
}
return true;
}
4:把方法a和方法b比对很多次来验证方法a是否正确
5:如果有一个样本使得比对出错,打印样本分析是哪个方法出错
public static void main(String[] args) {
int testTime=10000;
int size=20;
int value=50;
boolean isSucceed = true;
for (int i = 0; i < testTime; i++) {
int[] a1 = generateRandomArray(size, value);
int[] a2 = copyArr(a1);
int[] a3 = copyArr(a1);
bubblesort2(a1);
rightMethod(a2);
if (!isEqual(a1, a2)) {
isSucceed = false;
//打印出出现bug的样本
System.out.println("发生错误的样本:"+Arrays.toString(a3));
break;
}
}
System.out.println(isSucceed ? "↓ Good Job!" : "fail");
int[] arr = generateRandomArray(size, value);
System.out.println(Arrays.toString(arr));
bubblesort2(arr);
System.out.println(Arrays.toString(arr));
}
6:当样本数量很多时比对测试依然正确,可以确定方法a已经正确
附:copyArr方法
//拷贝数组 public static int[] copyArr(int[] arr) { if (arr == null) { return null; } int[] res = new int[arr.length]; for (int i = 0; i < arr.length; i++) { res[i] = arr[i]; } return res; }
对数器测试结果
时间复杂度
冒泡排序的时间复杂度按照最坏情况来说是:O(N²)。
算法稳定性
冒泡排序是两两比较,不满足要求的才会交换。两个相同元素的前后顺序不会发生变化,所以冒泡排序是一种稳定排序算法。