主要排序算法的比较及其优化

一,目的
本节内容主要讲了冒泡排序,插入排序,希尔排序,快速排序,并且提供了它们排序速度的比较,以及各自的优化。

二,主要排序算法
1,冒泡排序
冒泡排序又称起泡排序(bubble),之所以称为起泡排序,是因为每次排序关键字较小的元素像水中的气泡一样逐步向上漂浮,而关键字较大的元素像石块一样下沉。
请看下图:
起泡排序
从上图我们可以看到:第一次,挑出9,第二次排出8,第三次,第四次。。。是不是特别像鱼吐泡泡啊?小的元素慢慢地浮到上面了。
冒泡排序思路:将序列中的第一个元素与第二个进行比较,如果第一个比第二个大,则交换,再将第二个与第三个比,如果第二个不大于第三个元素则不用交换,直到第n-1个元素与第n个元素比较,这样每次找出最大的放到相应的位置上。第二次外层循环再在n-1个元素中找出最大的放到第n-1个位置,直到只剩下一个元素没有排为止。
举例:有数列 11,6,4 ,9 ,1,5,7,4
11比6大交换,当前为11,再与4进行比较,11大于4,交换,当前为11,第三次比较的数为9,11比9大,交换,所以继续找下一个数。
代码实现:

void bubble_sort(int* arry, int n)
{
 forint i=n-1;i>0;i--) //外循环控制次数
  {
   for(int j=0;j<i;j++) //内循环进行比较
    {
     if(arry[j]>arry[j+1])
       swap(arry[j],arry[j+1]);
    }
  } 

}

其中arry是待排数组的指针,N为数组长度,swap用来交换两个数。通过分析可得,冒泡排序的时间复杂度为:O(N*N),并且是稳定排序,因为,只有当比较的元素比当前元素小时才会进行交换,所以相等的两个数是不会产生交换行为的。
1.1 冒泡排序的优化。
冒泡排序的关键便是与后面的元素进行比较,然后进行交换,但是当我们发现某次排序的过程中都没有发生过交换的行为,那么便可以认为,剩下的元素都是有序的,所以也就不用再比较下去。
例如数列:1,2,3,4,5,6,7,8,9;
进行第一次排序我们发现后面的数列2大于1,3大于2,4大于3。。。9大于8就没有进行交换的行为,此时便可以认为是有序的,而不用再进行比较

1.2 优化代码实现。
思路:每次进行内部循环,设置个flag用来标记此次循环有没有发生交换。

void bubble_sort(int* arry, int n)
{
    for (int i = n-1; i >0; i--) //循环n-1次
    {
        bool flag = false;
        for (int j = 0; j<i; j++)
        {
            if (arry[j]>arry[j+1])
            {
                swap(arry[j], arry[j+1]);
                flag = true; //有交换置标志位为真
            }
        }
        printArry(arry, n);
        if (flag == false)
           break;
    }
}

2,快速排序
在实际运用中冒泡排序使用的频率及其低,因为每次排序它都要经过大量的交换,十分费时间,但在它的基础上实现的快速排序,却有着很好的效率。顾名思义。

快速排序的实现使用了分治的思想:
1,找一个基准值(通常情况下选择待排序列中的第一个或最后一个元素)
2,将序列中比基准值大的移到其右边,比它小的移到它的左边
3,将基准值左边的新序列和右边的新序列分别进行1,2步,直到待排序列元素只剩下一个。
在实现的过程中用到了递归。
举例说明:5,4,1,8 ,10,7,9,2,4,3(刚开始)
基准值选:5
进行比较:(3,4,2,1,4)5(7,9,10,8)
基准值选3,7
分别为:(2,1)3(4,4)
7(9,10,8)
以此进行下去,当划分为一个元素时便终止,则整个待排序列都为有序的了,快排平均时间复杂度O(N*logN)

2.1 代码实现

void quick_sort(int* arry,int size,int begin, int end)
{
    if (begin>=end)  //结束条件
        return;
    int i = begin, j = end;
    int temp = arry[i]; //基准值的选取
    while(i < j)
    {
        while (j> i&& arry[j] > temp)
        {
            j--;
        }
        if (j>i) //当比基准值小时
        {
            swap(arry[i], arry[j]); //交换
            i++;
        }
        while (i<j && arry[i] <=temp)
        {
            i++;
        }
        if (i<j) //当比基准值大时
        {
            swap(arry[j], arry[i]);
            j--;
        }
    }
    arry[i] = temp;
    quick_sort(arry, size, begin, i - 1); //递归进行
    quick_sort(arry, size, i + 1, end);
}

快排平均时间复杂度为O(N*logN),但当基准值选择不当便会较低到O(N*N);
这是因为,当基准值选取恰好为当前序列中最小的时候,便意味着每次排序都只是对1和N-1个数进行排序,则效率将大大降低,例如:
数列:1,7,8,9,4,2,5,3;
当选取1为基准值,你会发现没有比1小的,则需要比较n-1次,则对N个数进行排序大概需要O(N*N);
另一种情况:当待排序列为有序时。
改良快排的这种情况,主要有两个方法:
 1,随机选取基准值法
 2,三数取中法(将序列中的第一个,中间那个,最后一个进行大小比较,挑出排在中间的那个数为基准值)

现在我主要针对随机选取基准值法进行说明,每趟快排的时候我们可以使用随机数法选择基准值,这样大大降低了基准值挑选不当,以及待排序列为有序的情况。
代码如下:

int getRand(int i, int j)  //产生[i,j)的随机数
{
    srand((unsigned)time(NULL));
    int x = rand() % (j - i) + i;
    return x;
}
void quick_sort_practice(int* arry,int size,int begin,int end)
{
    assert(arry != NULL);
    if (begin >= end) //结束条件
        return;
    int i = begin, j=end;
    int pos = getRand(i, j); //产生基准值
    swap(arry[i], arry[pos]);
    int temp = arry[i]; //以左边的元素为基准值
    while (i < j)
    {
        while (j > i && arry[j] > temp)
        {
            j--;
        }
        if (j > i)
        {
            swap(arry[i], arry[j]);
            i++;
        }
        while (i < j && arry[i] <= temp)
        {
            i++;
        }
        if (i < j)
        {
            swap(arry[j], arry[i]);
            j--;
        }
    }
    arry[i] = temp;
    quick_sort_practice(arry, size, begin, i - 1);
    quick_sort_practice(arry, size, i + 1, end);

}

3,插入排序及希尔排序在我的博客中已经有详细的介绍,所以现在不在进行详述。

三,排序效率的比较
请直接看结果:
这里写图片描述

上图是每种情况的测试时间,具体单位其实并不重要,这只是用来进行比较的一种策略。
第一行:
测试1千个数据从小到大排列所需的时间,数据用随机数产生(每种排序算法排的随机数都是相同的,下同),范围:【0,10000)

第二行:
测试1万个数据从小到大排列所需时间,范围【0,10000);

第三行:
测试1千个数据从小到大排列所需时间,范围【0,1000);

第四行:
测试1千个数据从小到大排列所需时间,范围【0,500);

第五行:
测试1千个数据从小到大排列所需时间,范围 【0,10000);

第六行
测试2千个已排好序列排序所用时间,范围【0,100000);

注:以上测试结果还与测试所用的机型,测试的方法有关,并且所有类型的测试我都只测试了一遍,按要求应该测多次,然后求平均数来计量的。

四,总结
1,经过测试我们发现在大部分情况下快速排序的效率都是极其高的,但细心的同学可能发现当测试的数据为有序的时候,快排的效率便会降低,并且有可能造成栈溢出等问题。

2,插入排序整体上比冒泡排序和希尔排序的效率高

3,冒泡排序速度最慢。

4,工作生活中有好多地方都设计排序的问题,学好排序的相关知识很有用。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值