面试中的排序算法总结(二)

4,希尔排序
希尔排序又称为缩小增量排序,它是一种插入排序,它是直接插入排序算法的加强版。
希尔排序的基本思想:把记录按步长gap分组,对每组记录采用直接插入排序方法进行排序。随着步长逐渐减少,所分成的组包含的记录越来越多,当步长的值减小到1时,整个数据合成为一组,构成一组有序记录,则完成排序。

例如: 已知序列{24,8,30,50,42,19,21,40,11,15},要求:用希尔排序法排序,增量序列为:5、3、1,写出每趟的结果

增量为5时,我的理解是:将第一个和它之后的第5个比较,依次第二个和它后面的第5个,依次,直到结束。
这里写图片描述
最后增量为1时,就是简单排序了,相邻的两个互相比较,得到最后的结果。
算法稳定性:由上图可知,希尔排序中相同的数据也可能会交换位置,因此希尔排序为不稳定的算法。
时间复杂度:平均情况为O(Nlog2N)
C语言代码如下(摘自百度)

#include<stdio.h>
#include<math.h>

#define MAXNUM 10

void main()
{
    void shellSort(int array[],int n,int t);//t为排序趟数
    int array[MAXNUM],i;
    for(i=0;i<MAXNUM;i++)
        scanf("%d",&array[i]);
    shellSort(array,MAXNUM,int(log(MAXNUM+1)/log(2)));//排序趟数应为log2(n+1)的整数部分
    for(i=0;i<MAXNUM;i++)
        printf("%d ",array[i]);
    printf("\n");
}

//根据当前增量进行插入排序
void shellInsert(int array[],int n,int dk)
{
    int i,j,temp;
    for(i=dk;i<n;i++)//分别向每组的有序区域插入
    {
        temp=array[i];
        for(j=i-dk;(j>=i%dk)&&array[j]>temp;j-=dk)//比较与记录后移同时进行
            array[j+dk]=array[j];
        if(j!=i-dk)
            array[j+dk]=temp;//插入
    }
}

//计算Hibbard增量
int dkHibbard(int t,int k)
{
    return int(pow(2,t-k+1)-1);
}

//希尔排序
void shellSort(int array[],int n,int t)
{
    void shellInsert(int array[],int n,int dk);
    int i;
    for(i=1;i<=t;i++)
        shellInsert(array,n,dkHibbard(t,i));
}

5,快速排序
快速排序是对冒泡排序的一种改进。
基本思想就是:通过一趟排序将要排序的数据分割成独立的两个部分,其中一部分的所有数据要比另外一部分的所有数据都要小,然后在按照此方法对这两部分数据分别进行快速排序,以此达到整个数据变成有序序列。
例如: 已知序列{24,8,30,50,42,19,21,40,11,15},要求:用快速排序法排序,写出每趟的结果
解析:以24为基准,先从右向左找到第一个比24小的数,交换位置。
这里写图片描述
然后从左向右找到第一个比24到的数,交换位置。得到如下
15 8 24 50 42 19 21 40 11 30
继续比较,直到向左和向右碰头,找到同一个数为止。如图:
这里写图片描述
此时第一次排序结束。但是不会得到最后的结果。需要对24左边的数据和右边的数据进行快速排序。才会得到最终快速排序的结果。
快速排序的平均时间复杂度为:O(NlogN)
快速排序算法是比较高效的排序算法。

void sort(int *a, int left, int right)
{
    if(left >= right)/*如果左边索引大于或者等于右边的索引就代表已经整理完成一个组了*/
    {
        return ;
    }
    int i = left;
    int j = right;
    int key = a[left];

    while(i < j)                               /*控制在当组内寻找一遍*/
    {
        while(i < j && key <= a[j])
        /*而寻找结束的条件就是,1,找到一个小于或者大于key的数(大于或小于取决于你想升
        序还是降序)2,没有符合条件1的,并且i与j的大小没有反转*/ 
        {
            j--;/*向前寻找*/
        }

        a[i] = a[j];
        /*找到一个这样的数后就把它赋给前面的被拿走的i的值(如果第一次循环且key是
        a[left],那么就是给key)*/

        while(i < j && key >= a[i])
        /*这是i在当组内向前寻找,同上,不过注意与key的大小关系停止循环和上面相反,
        因为排序思想是把数往两边扔,所以左右两边的数大小与key的关系相反*/
        {
            i++;
        }

        a[j] = a[i];
    }

    a[i] = key;/*当在当组内找完一遍以后就把中间数key回归*/
    sort(a, left, i - 1);/*最后用同样的方式对分出来的左边的小组进行同上的做法*/
    sort(a, i + 1, right);

6,基数排序
基数排序属于“分配式排序”,又称“桶子法”。顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog(r)m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。
例如: 已知序列{24,8,30,50,42,19,21,40,11,15},要求:用基数排序法排序,写出每趟的结果
首先根据个位数的数值,在走访数值时将它们分配至编号0到9的桶子中:
这里写图片描述
接着再进行一次分配,这次是根据十位数来分配:
这里写图片描述
这时候整个数列已经排序完毕;如果排序的对象有三位数以上,则持续进行以上的动作直至最高位数为止。
LSD的基数排序适用于位数小的数列,如果位数多的话,使用MSD的效率会比较好。MSD的方式与LSD相反,是由高位数为基底开始进行分配,但在分配之后并不马上合并回一个数组中,而是在每个“桶子”中建立“子桶”,将每个桶子中的数值按照下一数位的值分配到“子桶”中。在进行完最低位数的分配后再合并回单一的数组中。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值