冒泡排序:
基本思想:
有需要排序的数组array[1......N]竖直排列,将每个元素array[i]看做是一个气泡,则重气泡下沉,轻气泡上升。每次比较相邻的两个气泡array[i]和array[i+1]。如果array[i]大于array[i+1],则交换两个气泡,否则继续比较array[i+1]和array[2]。重复上述步骤,直到所有的气泡都是升序排列的。
假如有以下数组:
int a[6]={1,3,2,5,-1,6};
第一趟:
第一次冒泡:1 3 2 5 -1 6
第二次冒泡:1 2 3 5 -1 6
第三次冒泡:1 2 3 5 -1 6
第四次冒泡:1 2 3 -1 5 6
第五次冒泡:1 2 3 -1 5 6
这样子,就找到最重的气泡6,然后按照相同的方法找到第二重、第三重...气泡。从上面可以看出,实现这个排序需要两重循环,第一层控制趟数,第二层控制要排序的数据范围,若有n个数据,则:
第0趟:比较第0到第n-1个,下标从0到n-2;
第1趟:比较第0到第n-2个,下标从0到n-3;
...
第 i 趟:比较第0到第n-i-1个,下标从0到n-i-2;
...
第n-2趟:比较第0到第1个,下标从0到0。
源码如下:
//冒泡排序
void bubblesort(int a [], int len){
for (int i = 0; i < len; i++){
for (int j = 1; j < len - i; j++){
if (a[j]<a[j -1]){
swap(a[j], a[j - 1]);
}
}
}
}
//交换两个整数
void swap(int& a, int& b){
int tmp = a;
a = b;
b = tmp;
}
这种算法下,冒泡排序的最好、最坏情况,时间复杂度都是O(n^2),所以平均时间复杂度也是O(n^2)。
冒泡排序改进1:
其实我们可以看出上述算法有一点问题,如果在某一趟排序中没有发生数据交换,那么说明待排序的无序区中的所有元素均是有序的,那么冒泡排序就可以在这一趟扫描之后就结束。比如,如果一组数据本身就是有序的,那么第一趟比较时未发生数据交换,那么冒泡排序就可以结束了。
基于这种思想,我们设置一个标志位,在没有发生交换时,标志位设置为true,说明数据已经排好序,就不需要进行余下的循环;如果标志位为false,那么就进行余下的循环。
源码如下:
//改进的冒泡排序1:不做无用功
void bubblesortmodify_1(int a [], int len){
bool isSorted = false;
for (int i = 0; i < len&&!isSorted; i++){ //在没有排序的情况下才会循环
isSorted = true; //设定排序标志
for (int j = 1; j < len - i; j++){
if (a[j]<a[j-1]){
isSorted = false; //如果没有排序,则重新设置排序标志
swap(a[j], a[j - 1]);
}
}
}
}
从上面可以看出,如果数据(n个)的初始状态就是有序的,那么只需要进行1趟排序,n-1次比较,这是最好的情况。如果数据的初始状态是逆序的,那么就需要进行n-1趟排序,每趟要进行n-i次比较,总共要n(n-1)/2次比较。所以最好的情况时间复杂度是O(n),最坏的情况时间复杂度是O(n^2),平均下来还是O(n^2)。
冒泡排序改进2:
我们可以发现,在每趟的排序中,最后一次交换发生的位置last之前的相邻数据已经是有序的,所以下一趟排序开始时,0到last已经是有序的,而只有last到n-1是无序的,所以需要尽可能缩小无序的范围。
源码如下:
//改进的冒泡排序2:记录犯罪现场
void bubblesortmodify_2(int a [], int len){
int k = len - 1;
int i, j;
while (k != 0){
for (i = j = 0; j < k; j++){
if (a[j]>a[j + 1]){
swap(a[j], a[j + 1]); i = j;
}
}
k = i;
}
}
同样,如果数据(n个)的初始状态就是有序的,那么只需要进行1趟排序,n-1次比较,这是最好的情况。如果数据的初始状态是逆序的,那么就需要进行n-1趟排序,每趟要进行n-i次比较,总共要n(n-1)/2次比较。所以最好的情况时间复杂度是O(n),最坏的情况时间复杂度是O(n^2),平均下来还是O(n^2)。