您的位置:首页 > 其它

排序算法之冒泡排序

2013-11-08 11:12 162 查看
冒泡算法的实现思想比较简单,从待排序数组的首个元素开始扫描数组,若遇到当前元素大于他的后继元素,则交换两者顺序(降序的话判断条件相反),每一遍遍历均有一个最小的元素排至前端,就想冒泡一样,较轻的元素慢慢升上来。时间复杂度为O(n^2),空间复杂度为O(1)

版本一:基础的冒泡排序

void EX_SortBubble(int a[], int n)
{
    for (int i = 0; i < n; i++) {
        for (int j = n - 1; j > 0; j--) {
            if (a[j-1] > a[j]) {
                int temp = a[j];
                a[j] = a[j-1];
                a[j-1] = temp;
            }
        }
    }
    return;
}

以上便是最基本的冒泡排序程序,它完整地扫描了两遍数组,与数组的状态没有任何关系,不管是乱序还是有序,总是一丝不苟地完成所有的扫描。,实际上,我们可以偷懒那么一点点,因为当数组有序,是不需要交换的,也就是说,如果中间的某次扫描发现数组已经有序了,那么就不需要进行后面的扫描了。毕竟,真正需要完成n^2次交换的,只有最糟情况,即数组逆序,这种情况很少的。所以可以做个小小的改进,当发现无需交换时,停止扫描。于是有了下一个版本。

版本二:带有交换标志的冒泡排序

void EX_SortBubbleWithFlag(int a[], int n)
{
for (int i = 0; i < n; i++) {
int exchange = 0;
for (int j = n - 1; j > 0; j--) {
if (a[j-1] > a[j]) {
exchange = 1;
myexchange(a,j,j-1);
}
}
if (0 == exchange) {
break;
}
}
return;
}

当某次发现数组已经排好序不需要交换是,就无需后面的扫描与比较了,循环之前交换标志置0;若发生交换,标志变为1。无交换说明数组已经排好序。这样就会节省后面无谓的比较时间,那么,还能不能再一步偷懒呢~~再想想,上面呢,只是整个数组有序的时候,才不会继续遍历比较,那么如果局部有序呢,能不能也跳过这一段已经有序的,从他的后面开始比较呢?嗯嗯,不错的想法,这样做完全没有问题。还是举个例子吧,比如数组a[] = {3,2,1,6,23,4,7,90,45,9},对其进行排序,过程如下:

第一次(i=0):1、3、2、4、6、23、7、9、90、45;很正常,没什么事发生(i = 0)

第二次(i=1):1、2、3、4、6、7、23、9、45、90;(也正常,没什么事发生)

第三次(i=2):这次问题就出来了,进行到这一步,我们认为前两个元素,即1、2是有序的,这次从3开始比较,也就是从90比较到3,但实际上我们发现前面的元素已经局部有序了,于是某些元素就可以不用比较了,那么,问题就来了,怎么确定新的比较起始点呢?因为每次比较,都将最小的元素上浮,因此当某个元素上浮到某个位置时,发现它不能再上浮了(没有产生交换),于是标志它已经到了自己合适的位置并且前面的元素均有序。比如上面数列的元素7,当它和23交换后,便到了适合自己的位置,那么我们下次就可以从23开始比较了,因此7后面的元素均大于7。23是个什么位置呢,对了,是本次扫描最后一次发生交换的位置。这就是说,该位置前面的没有发生交换,都已经有序了。常规的说,不管是第一个版本还是第二个版本的排序,进行到这一步仍然需要排序数组2~9位置的元素,但是这次我们直接可以排序数组6~9的元素,是的,我们跳过了中间的元素。但它是没有错误的。我们记录最后一次比较的位置,这个时候,版本二中的bool变量exchange就有两个功能:记录最后一次交换的位置
and 是否交换的标志。虽然一个变量表示两个意义不是很好,但是如果能简化问题的话,呵呵~~这里有个需要注意的地方,最后一次交换不可能发生在位置0,因此最后一次比较是用位置1和位置0,如果交换,我们记录交换位置为1,而不是0,这样,exchange = 0就可以用来判断是否发生交换了。

版本三:蹦蹦跳跳的标志

void EX_SortBubbleWithFlag(int a[], int n)
{
    int exchange = 1;   //初始化exchange为1
    while (exchange) {
        int bound = exchange;  //从最后一次交换发生出开始新的遍历
        exchange = 0;
        for (int i = n - 1; i >= bound; --i) {
            if (a[i] < a[i - 1]) {
                myexchange(a, i, i-1);
                exchange = i;     //有交换,更新位置
            }
        }
    }
    return;
}


这样就可以省去一些比较的次数了,我们可以统计下比较次数,很简单,声明一个static变量,在if条件内使用逗号表达式,可以计算比较次数,以文中所举数列为例,三个版本的比较次数分别为90,36,29.
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: