冒泡排序属于交换排序,通过元素间的比较和交换位置来达到排序目的。
冒泡排序的每一轮只把一个元素冒泡到数列的一端,从序列左边开始比较相邻两个数字的大小,根据结果交换两个数字的位置。
优点:稳定;
缺点:慢,每次只能移动两个相邻的数据。
最佳情况:O(n)
最差情况:O(n^2)
平均情况:O(n^2)
空间复杂度:O(1)
比较次数:(n-1) + (n-2) + … + 1 = n^2/2
public <T extends Comparable<T>> T[] sort(T[] array) {
int len = array.length;
for (int i = 0; i < len - 1; i++) {
System.out.format("第%d遍:\n", i + 1);
for (int j = 0; j < len - i - 1; j++) {
if(less(array[j + 1], array[j])) {
swap(array, j, j + 1);
}
System.out.format("第%d遍的第%d次交换:", i + 1, j + 1);
print(array);
}
System.out.println();
}
return array;
}
当我们输入数组:[1, 3, 6, 2, 9],通过上面程序可以看到输出:
第1遍:
第1遍的第1次交换:[1, 3, 6, 2, 9]
第1遍的第2次交换:[1, 3, 6, 2, 9]
第1遍的第3次交换:[1, 3, 2, 6, 9]
第1遍的第4次交换:[1, 3, 2, ***6, 9***]
第2遍:
第2遍的第1次交换:[***1, 3***, 2, 6, 9]
第2遍的第2次交换:[1, ***2, 3***, 6, 9]
第2遍的第3次交换:[1, 2, ***3, 6***, 9]
第3遍:
第3遍的第1次交换:[***1, 2***, 3, 6, 9]
第3遍的第2次交换:[1, ***2, 3***, 6, 9]
第4遍:
第4遍的第1次交换:[***1, 2***, 3, 6, 9]
由上看出,经过第二遍后,数组已经有序了,但算法还是继续执行到了第4遍才完成。
因此,可以通过添加一个标记,标识数列已经有序了,然后跳出循环。
代码如下:
public <T extends Comparable<T>> T[] sort(T[] array) {
int len = array.length;
for (int i = 0; i < len - 1; i++) {
System.out.format("第%d遍:\n", i + 1);
boolean isSorted = true;
for (int j = 0; j < len - i - 1; j++) {
if(less(array[j + 1], array[j])) {
swap(array, j, j + 1);
// 有元素位置交换,序列不是有序的
isSorted = false;
}
System.out.format("第%d遍的第%d次交换:", i + 1, j + 1);
print(array);
}
System.out.println();
if(isSorted) {
break;
}
}
return array;
}
交换过程如下:
第1遍:
第1遍的第1次交换:[1, 3, 6, 2, 9]
第1遍的第2次交换:[1, 3, 6, 2, 9]
第1遍的第3次交换:[1, 3, 2, 6, 9]
第1遍的第4次交换:[1, 3, 2, 6, 9]
第2遍:
第2遍的第1次交换:[1, 3, 2, 6, 9]
第2遍的第2次交换:[1, 2, 3, 6, 9]
第2遍的第3次交换:[1, 2, 3, 6, 9]
第3遍:
第3遍的第1次交换:[1, 2, 3, 6, 9]
第3遍的第2次交换:[1, 2, 3, 6, 9]
从上面的过程可以发现,是有优化了,但还是会有问题。第二遍开始已经有序了,但算法没发现。
上面问题根本原因是算法并不知道数列有序去的开始位置,
为解决这个问题,我们可以记录最后一次元素交换的索引,这个索引之后的序列是有序的。
代码:
public <T extends Comparable<T>> T[] sort(T[] array) {
int len = array.length;
int rightIndex = len - 1;
int lastSwapIndex = 0;
for (int i = 0; i < len - 1; i++) {
System.out.format("第%d遍:\n", i + 1);
boolean isSorted = true;
for (int j = 0; j < rightIndex; j++) {
if(less(array[j + 1], array[j])) {
swap(array, j, j + 1);
// 有元素位置交换,序列不是有序的
isSorted = false;
lastSwapIndex = j;
}
System.out.format("第%d遍的第%d次交换:", i + 1, j + 1);
print(array);
}
System.out.println();
rightIndex = lastSwapIndex;
if(isSorted) {
break;
}
}
return array;
}
鸡尾酒排序属于冒泡排序的一种升级。
不同于冒泡排序的每一轮都从左到右进行比较元素,进行单向的位置交换,鸡尾酒排序的元素比较和交换都是双向的。
public <T extends Comparable<T>> T[] sort(T[] array) {
int len = array.length;
for (int i = 0; i < len/2; i++) {
System.out.format("第%d遍:\n", i + 1);
boolean isSorted = true;
for (int j = i; j < len - i - 1; j++) {
if(less(array[j + 1], array[j])) {
swap(array, j, j + 1);
// 有元素位置交换,序列不是有序的
isSorted = false;
}
System.out.format("第%d遍的第%d次交换:", i + 1, j + 1);
print(array);
}
if(isSorted) {
break;
}
isSorted = true;
for (int j = len -i - 1; j > i; j--) {
if(less(array[j], array[j - 1])) {
swap(array, j, j - 1);
// 有元素位置交换,序列不是有序的
isSorted = false;
}
System.out.format("第%d遍的第%d次交换:", i + 1, j + 1);
print(array);
}
System.out.println();
if(isSorted) {
break;
}
}
return array;
}