几种常用的排序算法(冒泡,插入排序,希尔排序,快速排序)

排序算法有很多,也各有优点,随着计算机处理速度的提升,小数据量的排序任何一种排序算法都能在很短很短的时间内完成,如果数据量很小,我们只需要选择较为简单的排序算法,如冒泡排序和插入排序,而当数据量较大时,依然需要优秀的排序算法,本文主要介绍了快速排序和希尔排序算法:

一 冒泡排序(bubble sort)

实现思想:

1、从列表的第一个数字到无序序列的倒数第二个数字,逐个检查:若某一个位上的数字大于他的下一位,则将它与它的下一位交换,一趟冒泡后最后一个数为最大值,下次冒泡不用考虑

2、重复1步骤,直至再也不能交换

    冒泡排序总的比较次数为:(n-1)+(n-2)...+1=n*(n-1)/2

    时间复杂度:O(n^2)(数量级)

void bubble_sort(int arr[], int n)
{
    int temp;
    for(int i=0; i<n-1; i++)//循次n-1趟
        for(int j=0; j<n-i-1; j++)//第i趟两两比较的次数为n-i-1,最终将最大数移到末尾
        {
            if(arr[j]>arr[j+1])
            {
                temp = arr[j];
                arr[j]=arr[j+1];
                arr[j+1]=temp;
            }
        }
}


二、插入排序 (insert sort)

实现思想:

插入排序类似整理扑克的过程,从第二张牌开始依次和手中已有的牌比较并插入到合适的位置。假设某一次我们起的牌凑巧从小到大出现,我们只需要放在最后就可以了,是不是很爽,不错,这就是插入排序最快的场合(数据基本有序)

 最坏时间复杂度:O(n^2)--待排序的数组为逆序(每插入一个数据时都会多次比较并移动比其大的数据)

    最好时间复杂度:O(n)--待排序的数组为正序
void insert_sort(int arr[], int n)
{
    int i,j;
    int temp;
    for(i=1; i<n; i++)//依次从第二个元素开始取
    {
        temp=arr[i];
        for(j=i; j>0&&temp<arr[j-1]; j--)//取出的元素依次与前面的元素比较并将其插入合适的位置
            arr[j]=arr[j-1];
        arr[j]=temp;
    }
}

冒泡排序和插入排序的比较:
插入排序的时间复杂度略优于冒泡排序。
原因:一般情况插入排序的时间复杂度为二者之间,当待插入的某个数为相对较大时(至少为最小值的概率小),经过很少的几次比较就可插到合适的位置,省去了很多比较过程,而冒泡排序会始终进行比较。另外有一种改进的冒泡排序:当某一趟两两比较过程中一次交换都未发生,表面数组已经有序可以结束排序,这种情况出现的机会大吗?
    假设有这样一组随机8个数据:4 3 1 8 2 9 4 2(一般冒泡排序需要7趟)
    第一趟冒泡后:3 1 4 2 8 4 2 9
    第二趟冒泡后:1 3 2 4 4 2 8 9
    第三趟冒泡后:1 2 3 4 2 4 8 9
    第四趟冒泡后:1 2 3 2 4 4 8 9
    第五趟冒泡后:1 2 2 3 4 4 8 9---到此已经有序,下一趟检测并未有交换即可退出排序。(还是有一定的效率提高)
    这样谁的效率高呢?
    依然是插入排序略优,实际上插入排序比较次数直接为逆序对的个数,而冒泡排序的交换次数等于逆序对的个数,还有一些
    其他无意义的比较操作,因此插入排序的执行时间至少比冒泡排序快。(给定一个无序数组,逆序对固定)

    

三 、希尔排序

实现思想:

希尔排序是插入排序的改进,其实插入排序是一个很聪明的行为,之所以排序时间较长的主要原因是某些数据的影响。例如对十万个数据进行插入排序过程中,如果最后一个数为十万个数中最小的,因为这一个数使这些数据的逆序对增加了十万,前面说过逆序对直接影响了排序的比较次数。如果能够在插入排序前进行一些预排列,使其基本有序。这时最后一步的插入排序速度会出现质的飞跃。这就是希尔排序的算法思想。

希尔排序将序列将具有某个固定间隔的数据分为一组,分别对每一组中的数据进行插入排序,然后再缩小间隔重复此过程。。。直到间隔为1,做一次普通插入排序即可。。比如10万个数据量的数据,首先将间隔设置为50000,分为50000组,然后再将间隔设置为25000,分为25000组,而插入排序对基本有序的数据排序效率很高,所以希尔排序的过程正好是数据逐渐有序的过程。当然希尔排序还有其他的增量序列选择方式。

void shell_sort(int arr[], int n)
{
    int gap=n/2;
    while(gap>=1)
    {
        for(int i=gap;i<n;i++)//将每组从第二个元素开始插入到组中合适的位置
        {
            int j=0;
            int temp=arr[i];
            for(j=i-gap;j>=0&&temp<arr[j];j=j-gap)
                arr[j+gap]=arr[j];
            arr[j+gap]=temp;
        }
        gap=gap/2;
    }
}

四、快速排序(quick sort)

实现思想:

     快速排序采用的分而治之的思想利用递归来实现的,首先从待排数据中选择一个主元,依次与其他元素比较,并以主元大小为基础将数据分为两半,然后分别对左右两个序列再进行排序。主元的选择有很多方法,常用的为直接选择序列中的第一个元素作为主元。还有其他选择方法自行研究。

快速排序最好的情况是每次选取的主元恰好为待排序列的中点,即排号序该序列恰好被主元分成两个相等的序列。

快速排序最坏的情况是每次选择的主元为待排序列的最小或最大,这样划分后的序列一个为空,一个为序列个数-1,这样就和冒泡没什么区别了

因此,快速排序:

最好时间复杂度:O(NlogN)

     最坏时间复杂度:O(N^2)

void quick_sort1(int arr[], int left, int right)
{    
        if(left<right)
        {
            int pivot=arr[left];//选取主元
            int low=left, high=right;
            while(low<high)//序列划分
            {
                while(low<high&&arr[high]>=pivot) 
                    high--;
                arr[low]=arr[high];
                while(low<high&&arr[low]<=pivot) 
                    low++;
                arr[high]=arr[low];
            }
            arr[low]=pivot;
            quick_sort1(arr,left,low-1);//递归
            quick_sort1(arr,low+1,right);
        }
}

比较完插入排序和冒泡排序这两个小菜鸟,来比较下快速排序和希尔排序这两位重量级的排序算法,二者谁更好呢?
这个还真不能绝对的断言,只能说多数情况下快速更快一些,不过所谓的快也是同数量级的领先而已,我们知道快速排序的最好时间复杂度为O(NlogN),最坏时间复杂度为O(N^2),而希尔排序的时间复杂度取决于增量序列的选择,当选择某一固定的增量序列后希尔排序的时间复杂度比快速排序更稳定,受某些序列影响较小,比如,某一数据量为基本有序时,如果使用快速排序将会非常慢。总体来说快速排序还是略胜一筹,也是数据量较大场合下使用最多的一种排序算法。


四种算法速度测试:(ubuntu 16.0 g++编译器)

数据量为100时四种算法的排序时间:


数据量为1000时四种算法的排序时间:


数据量为10000时四种算法的排序时间:


数据量为100000时四种算法的排序时间:


总结:

由测试结果可知,当数据量为1000以下时,四种算法的区别并不太明显,而当数据量大于10000甚至更多时,快速排序和希尔排序的优越性得到充分证明,所以对于小数据量的排序,选择较为简单的冒泡和插入排序即可。而面对大数据量的排序必须采用优秀的排序算法。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值