排序算法之冒泡排序

来源:互联网 发布:短信恢复软件注册码 编辑:程序博客网 时间:2024/04/29 14:09

作者:hustwing         hustwing@126.com  MSN: hustwing@hotmail.com

冒泡排序算是排序算法中比较有名的了,初学排序之时,我们经常拿它来开刀,大学时学习C语言,老师讲的第一个排序算法就是冒泡。然而它的效率低也是众所周知的。只要说起冒泡,大家无不皱起眉头,仿佛用了冒泡就说明你这个人完全没有考虑效率。但是,其实又有多少人真的理解冒泡呢?

我之前根本没有准备在Algorithm里专门为冒泡写一篇文章,但是当我发觉我需要大概大半天的时间才能弄懂它时,我才觉得还是有必要把我所学的关于冒泡算法的知识总结一下。

起初,我搞混了冒泡排序跟选择排序,后来到网上一查,发现大概有一半的人跟我一样弄混了这两种算法。下面是这两种算法的基本思想:

选择法排序(升序)
基本思想:
  1)对有n个数的序列(存放在数组a(n)中),从中选出最小的数,与第1个数交换位置;
  2)除第1 个数外,其余n-1个数中选最小的数,与第2个数交换位置;
  3)依次类推,选择了n-1次后,这个数列已按升序排列。

冒泡法排序(升序)
基本思想:(将相邻两个数比较,小的调到前头)
1)有n个数(存放在数组a(n)中),第一趟将每相邻两个数比较,小的调到前头,经n-1次两两相邻比较后,最大的数已“沉底”,放在最后一个位置,小数上升“浮起”;
2)第二趟对余下的n-1个数(最大的数已“沉底”)按上法比较,经n-2次两两相邻比较后得次大的数;
3)依次类推,n个数共进行n-1趟比较,在第j趟中要进行n-j次两两比较。

从上面的基本思想可以看出,冒泡法与选择法最大的不同是,冒泡法是相邻的数比较交换,而选择法则是其它所有的数与某个特定位置的数比较交换。反映到代码上就是下面的区别;

---------------------------------简单选择排序-------------------------------------

void SimpleSelectSort(int* const arr,int arrLength)
{
    int i,j,imin,temp;

    for (i = 0; i< arrLength -1; i++)
    {
        for (j = i+1; j< arrLength;j++)
        {
            if (arr[i] > arr[j])//注意这一行,非相邻比较,选择排序
            {
                temp = arr[i];
                arr[i] = arr[j];
                arr[j] = temp;
            }
        }
    }
}

---------------------------------简单冒泡排序-------------------------------------

void SimpleBubbleSort(int* const arr,int arrLength)
{
    int i,j,temp;
    for (i = 0; i < arrLength -1; i++)
    {
        for (j = 0;j        {
            if (arr[j] > arr[j+1])//注意这一行,是相邻比较
            {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
            }
        }
    }
}

------------------------------------------------------------------------------------------

为其正名之后,我们再来看看它还有些什么变种,呵呵。按照其基本思想,我们可以这样设想:设若是升序排列,那我们就有两种选择,一种是两两相邻比较,选择每次将最小的数"冒"到最左边来(数组的最前端),这叫做前向冒泡或者正向冒泡!也可以选择每次将最大的数"冒"到最右边去,这个相应叫做后向冒泡或反向冒泡。当然,聪明人还想到了第三种方法,那就是两边同时"冒",一次冒出一个最大的和最小的,然后再一次冒出一个次大的和次小的(双向冒泡!)!事实证明这的确是个不错的想法,至少从效率上说是这样。

下面是这三种冒泡算法的具体实现代码,代码里包含了与性能比较有关代码,只有循环那部分的代码(粗体)是冒泡算法的具体实现,在文章的最后,给出关于这三种算法的性能测试,当然,这个数据是我自己测试得出,不见得绝对客观的。

--------------------------------------------------------------------三种冒泡算法---------------------------------------------------------------------

SORT_PERFORMANCE ForwardBubbleSort(int* const arr,int arrLength)
{
    int i,j,temp;
    bool flag = false;
    unsigned int circuTimes = 0,swapTimes = 0;
    clock_t start,end;
    SORT_PERFORMANCE sort_perform;

    start = clock();
   for (i = 0; i < arrLength -1; i++)
    {
        for (j = arrLength - 1;j > i; j--)
        {
            circuTimes++;
            if (arr[j] < arr[j-1])
            {
                temp = arr[j];
                arr[j] = arr[j-1];
                arr[j-1] = temp;
                flag = true;
                swapTimes++;
            }
        }
        if (!flag)
        {
            break;
        }
    }

    end = clock();
    sort_perform.CircuTimes = circuTimes;
    sort_perform.MilliSeconds = end - start;
    sort_perform.SwapTimes = swapTimes;
    return sort_perform;
}
SORT_PERFORMANCE BackwardBubbleSort(int* const arr,int arrLength)
{
    int i,j,temp;
    bool flag = false;
    unsigned int circuTimes = 0,swapTimes = 0;
    clock_t start,end;
    SORT_PERFORMANCE sort_perform;
    start = clock();
    for (i = 0; i < arrLength -1; i++)
    {
        for (j = 0;j        {
            circuTimes++;
            if (arr[j] > arr[j+1])
            {
                temp = arr[j];
                arr[j] = arr[j+1];
                arr[j+1] = temp;
                flag = true;
                swapTimes++;
            }
        }
        if (!flag)
        {
            break;
        }
    }
    end = clock();
    sort_perform.CircuTimes = circuTimes;
    sort_perform.MilliSeconds = end - start;
    sort_perform.SwapTimes = swapTimes;
    return sort_perform;
}
SORT_PERFORMANCE DoubleDirectionBubbleSort(int* const arr,int arrLength)
{
    int temp; 
    int left = 0; 
    int right =arrLength -1; 
    int t = 0,i; 
    unsigned int circuTimes = 0,swapTimes = 0;
    clock_t start,end;
    SORT_PERFORMANCE sort_perform;
    start = clock();
    do
    {
        //先正向冒泡
        for (i = right; i > left; i--)
        {
            circuTimes++;
            if (arr[i-1] > arr[i] )
            {
                temp = arr[i -1];
                arr[i -1] = arr[i];
                arr[i] = temp;
                t = i - 1;
                swapTimes++;
            }
        }
        left = t +1;

        //然后反向冒泡
        for (i = left; i < right; i++)
        {
            circuTimes++;
            if (arr[i] > arr[i+1])
            {
                temp = arr[i+1];
                arr[i+1] = arr[i];
                arr[i] = temp;
                t = i + 1;
                swapTimes++;
            }
        }
        right = t - 1;
    } while(left <= right);
    end = clock();
    sort_perform.CircuTimes = circuTimes;
    sort_perform.MilliSeconds = end - start;
    sort_perform.SwapTimes = swapTimes;
    return sort_perform;
}

---------------------------------------------------------------------------------------------------------------------------------------------------------

经过测试可以发现这三种冒泡方式的性能数据如下,其中引用的数据都是基于本机的,当然有些是不用测试也能猜到的,其中还有点比较有趣的地方:

1.正向冒泡和反向冒泡的性能是一样的,这一点我想不用测试也可以了解。

2.双向冒泡的性能要比前两种好一些,但是这种好处要在数据量比较大的时候才明显(5000个以上),在执行时间上可以比前两种快大概1/4,但是它的这种快并不想我在网上查到的资料所说的交换次数较少。而恰恰相反,交换次数与前两种一样,但是循环次数会比前两种冒泡减少1/3左右,时间就是从循环次数减少这里获得的。

3.双向冒泡一个比较有趣的地方是,当数据趋于有序时,它的效率也越来越高。2中的结论都是基于乱序(random)的数组测试得出的结论。当数组越来越有序时,它的循环次数较快地减少,所以效率也越来越高。但是当数组绝对有序时,它的循环次数是前两种的2倍!交换次数都是0。我测试过,当数组中只有一个元素无序时,它可以将循环次数节省99%以上(交换次数不变)!从而可以几乎100%的节省时间!但是一旦绝对有序,它的循环次数就上升到两倍!仿佛是物极必反,o(∩_∩)o...哈哈!

文章参考链接:http://www.zxbc.cn/html/20070425/7523.html

可以到这里下载本文的所有代码,包含性能测试部分(BubbleSort.cpp):http://download.csdn.net/source/590585