冒泡排序
定义
冒泡排序是一种简单的排序算法。它重复地走访过要排序的数列,一次比较两个元素,如果它们的顺序错误就把它们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。这个算法的名字由来是因为越小的元素会经由交换慢慢“浮”到数列的顶端。
算法描述
比较相邻的元素。如果第一个比第二个大,就交换它们两个;
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数; 针对所有的元素重复以上的步骤,除了最后一个;
重复步骤1~3,直到排序完成。
思路:消除相互紧邻的逆序的元素
方法:扫描交换
不变性:经过k轮扫描交换后,最大的k个元素必然就位
单调性:经k轮扫描交换后,问题规模缩减至n-k
正确性:经至多n趟扫描交换后,算法必然终止,且能给出正确答案
注:参数列表为(int a[], int n)是自己写的
以上就是冒泡排序的基本思想,按照这个定义很快就能写出代码:
//无优化
//冒泡排序的本质是通过两两比较将大数往后移动,每一趟迭代后将最大值、次大值...分别放到正确的位置
**public static void bubbleSort(int a[], int n) {
for (int i=n; i>0; i--) {
for (int j=1;j<n;j++) {//遍历n次
if (a[j] < a[j-1]) {
int temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
}
}
}
}**
改进1:
n* 根号n n是遍历数量,根号n是迭代趟数
依次比较每一对相邻元素,如有逆序,交换之;若整趟扫描都没有交换,则排序完成;否则再做一趟扫描交换
方法1(不易懂):
sorted = !sorted; 在下一趟扫描之前进行翻转,初始化为true
退出条件:翻转之前是true。(那上一次内层循环必然没有进行交换)
public class Sort {
public static void bubbleSort1(int A[], int n) {
//逐趟扫描交换,直至完全有序(一趟一趟的扫描交换)
for (boolean sorted = false; sorted = !sorted; n--) {//执行扫描交换
//内层:每次循环需要两两比较的次数,每次比较后,都会将当前最大的数放到最后位置,所以每次比较次数递减一次
for (int j = 1; j<n; j++ ) {
if (A[j-1] > A[j]) {
//若逆序看,交换数组array的j和j+1位置的数据
int temp = A[j-1];
A[j-1] = A[j];
A[j] = temp;
sorted = false;//清楚全局有序标志
}
}
}
}
public static void main(String[] args) {
int A[] = {5,3,6,9,88,12,89};
bubbleSort1(A, 3);
for (int a : A) {
System.out.println(a);
}
}
}
问题:该算法必然会结束,至多迭代多少趟?
不变性:经过k轮扫描交换后,最大定位k个元素必然就位
单调性:经过k轮扫描交换后,问题规模缩减至n-k
正确性:经至多n趟扫描后,算法必然终止,且能给出正确答案
方法二(容易理解):迭代进行low,high–之间的所有相邻元素,只要有一对相邻元素逆序,就进行交换
每一趟对bubble调用所需要时间都正比于前n-k个元素的长度。
public class Sort {
public static void bubbleSort2(int A[], int n) {
//逐趟扫描交换,直至完全有序
while (!bubble(A, 0, n--));
}
static boolean bubble(int A[], int low, int high) {
boolean sorted = true;//有序推论
while (++low < high) {
if (A[low-1] > A[low]) {
//若逆序看,交换数组array的j和j+1位置的数据
int temp = A[low-1];
A[low-1] = A[low];
A[low] = temp;
sorted = false;//清楚全局有序标志
}
}
return sorted;
}
public static void main(String[] args) {
int A[] = {5,3,6,9,88,12,89};
bubbleSort2(A, 7);
for (int a : A) {
System.out.println(a);
}
}
}
方法三:
public static void bubbleSort3(int a[], int n) {
int i = n;
boolean sorted = false;
while (!sorted) {//无序
sorted = true;
for (int j=1;j<n;j++) {//遍历n次
if (a[j] < a[j-1]) {
int temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
sorted = false;
}
}
i--;
}
}
改进2:
进一步做优化。比如,现在有一个包含1000个数的数组,仅前面100个无序,后面900个都已排好序且都大于前面100个数字,那么在第一趟遍历后,最后发生交换的位置必定小于100,且这个位置之后的数据必定已经有序了,也就是这个位置以后的数据不需要再排序了,于是记录下这位置,第二次只要从数组头部遍历到这个位置就可以了。如果是对于上面的冒泡排序算法改进1来说,虽然也只排序100次,但是前面的100次排序每次都要对后面的900个数据进行比较,而对于现在的排序算法改进2,只需要有一次比较后面的900个数据,之后就会设置尾边界,保证后面的900个数据不再被排序。
public static void bubbleSort3(int a[], int n) {
int last = n;
while (last > 0) {
last = 0;
for (int j = 1; j< n; j++) {
if (a[j] < a[j-1]) {
int temp = a[j];
a[j] = a[j-1];
a[j-1] = temp;
last = j;
}
}
}
}