八☞冒泡排序
冒泡排序
比较相邻的元素,如果前一个比后一个大,交换之。
第一趟排序第1个和第2个一对,比较与交换,随后第2个和第3个一对比较交换,这样直到倒数第2个和最后1个,将最大的数移动到最后一位。
第二趟将第二大的数移动至倒数第二位
…
因此需要n-1趟;
证明
对于每一次比较操作。若aj ≤ aj+1,aj, aj+1保持不变,于是aj ≤ aj+1;若aj > aj+1,则aj, aj+1互换位置,于是aj < aj+1。因此经过一次“比较”操作后恒有aj≤ aj+1。下面直接引用此结论。
对于每一次以下标1开始的循环,对j作数学归纳法。
进行了第j = 1次比较后,由上面结论知,aj≤ aj+1。所以aj+1是a1, …, aj, aj+1中最大的数。
假设当j = k 时“aj+1是a1, …, aj, aj+1中最大的数”成立。则当j = k + 1时,
由上面结论知,aj≤ aj+1,于是aj不超过aj+1。而根据假设aj是a1, …, aj-1, aj中最大的数。于是a1, …, aj, aj+1中最大的数是aj+1。
由数学归纳法原理可知。ai是a1, …, ai-1, ai中最大的数。
完成排序后,由刚才对i的讨论的任意性可知a2 = max(a1, a2), a3 = max(a1, …, a3), …, an = max(a1,…, an)。
因为a1≤ max(a1, a2) ≤ max(a1, …, a3) ≤ … ≤ max(a1, …, an),
所以a1≤ a2≤ … ≤ an。
证毕。
特点
- 优点:比较简单,空间复杂度较低,是稳定的
- 缺点:时间复杂度太高,效率不好
常规实现
public class BubbleSortNormal {
public static void main(String[] args) {
int[] list = {3,4,1,5,2};
int temp = 0;
// 要遍历的次数
for (int i = 0; i < list.length-1; i++) {
for (int j = 0; j < list.length-1-i; j++) {
// 比较相邻的元素,如果前面的数小于后面的数,就交换
if (list[j] < list[j+1]) {
temp = list[j+1];
list[j+1] = list[j];
list[j] = temp;
}
}
}
}
优化思路
从代码中我们可以发现,最原始的冒泡排序算法对相同规模的输入,其排序次数是固定不变的。然而经过一次次排序操作后,未排数据将趋于稳定(即有序),这是因为每次“冒泡”都是相邻元素进行比较和交换完成的。考虑到这种情况,我们便有了下面这种优化方案:
void bubbleSort1(int arr[], int n) {
boolean flag = true;//本次循环是否发生过数值交换,若false,则表示所有元素都有序
for (int i = 0; flag && i < n - 1; i++) {
flag = false;
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
flag = true;
}
}
}
}
第一种改进方案虽然有一定的优化效果,但只是考虑了尾端元素有序的情况,并没有考虑到元素局部有序这种情况,因此我们有了第二种改进方案:
void bubbleSort2(int arr[], int n) {
int lastSwapPos = n - 1;//最后一次交换的位置,此时后面数列顺序已经正确
int tempPos;
do {
tempPos = lastSwapPos;
lastSwapPos = 0;
for (int j = 0; j < tempPos; j++) {
if (arr[j] > arr[j + 1]) {
swap(arr[j], arr[j + 1]);
lastSwapPos = j;
}
}
} while (lastSwapPos > 0);
}