算法精解----19、归并排序、计数排序、基数排序

本文介绍了三种常用的排序算法:归并排序、计数排序和基数排序。归并排序是一种分治算法,适用于所有情况并能实现快速排序的平均性能,但需要额外的存储空间。计数排序是一种线性排序算法,通过计算整数出现的次数来排序,适用于整形数据。基数排序也是线性排序,按位数逐步排序,适合能够转换为整形的数据。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1、归并排序是另一种运用分治排序的算法,它的不同在于归并过程-将两个有序的数据集合合并成一个有序的数据集。
所有情况下都能达到快速排序的平均性能,但是需要额外的存储空间来运行,因为合并过程不能在无序数据本身运行。
会按照分的方式合,不用担心是否为2的幂。
这里写图片描述

//把排序好的两堆合并成一堆 
//i为头,k为尾;i 到 j;j+1 到 k 
static int merge(void *data, int size, int esize, int i, int k, int j, int (*compare)(const void *key1, const void *key2))
{
    char *a = data, *m;//m暂存排序好的有序数组 
    int ipos, jpos, mpos;
    ipos = i;
    jpos = j + 1;
    mpos = 0;
    if((m = (char *)malloc(esize *((k - i) + 1))) == NULL)//0到n是n+1个数 
        return -1;

    //从两组数的头开始比,两组都是排序好的数组,小的在前,依次把小的放入数组m
    //当 (ipos <= j) || ((jpos <= k)说明没比完 
    while((ipos <= j) || ((jpos <= k))
    {
        if(compare(&a[ipos * esize], &a[jpos * esize]) < 0)
        {
            memcpy(&m[mpos * esize], &a[ipos * esize], esize);
            ipos++;
            mpos++;
        } 
        else
        {
            memcpy(&m[mpos * esize], &a[jpos * esize], esize);
            jpos++;
            mpos++;
        }

        //左边都小,前面已经放完,现在放入右边 
        if(ipos > j)
        {
            while(jpos <= k)
            {
                memcpy(&m[mpos * esize], &a[jpos * esize], esize);
                jpos++;
                mpos++; 
            } 
        }
        else if(jpos > k)
        {
            while(ipos <= j)
            {
                memcpy(&m[mpos * esize], &a[ipos * esize], esize);
                ipos++;
                mpos++; 
            } 
        }
    }

    //把排好的数据放回原来数组对应区域 
    memcpy(&a[i * esize], m , esize*((k - i) + 1));
    free(m);
    return 0;

}
int mgsort(void *data, int size, int esize, int i, int k, int (*compare)(const void *key1, const void *key2))
{
    int j;
    if(i < k)
    {
        //当只剩下两个元素进入时(i=j,j+1=k),下面两个mgsort()不执行,直接执行merge() 
        j = (int)( ((i + k - 1)) / 2 );
        if(mgsort(data, size, esize, i, j, compare) < 0)
            return -1;
        if(mgsort(data, size, esize, j+1, k, compare) < 0)
            return -1;  
        if(merge(data, esize, i, j, k, compare) < 0)
            return -1;
    }
    return 0;
} 

2、计数排序
线性排序,不同于前面的比较算法,计数排序通过计算集合中整数出现的次数来决定集合应该如何排序。效率比O(nlgn)高,但是只能用于整形或者那些可以用整形来表示的数据集合。
这里写图片描述

//k是data中最大整数加1 ,0到k即k+1个数 
int ctsort(int *data, int size, int k)
{
    int *counts, *temp;
    int i, j;

    //0到k每个数对应一个数组位置记录其出现的次数,和上面图中不同。
    //这样data中数的大小直接对应计数数组中的位置,计数数组该位置对应的数字即data中数该放在排序数组中的位置 
    if((counts = (int *)malloc(k * sizeof(int))) == NULL)
        return -1;
    if((temp = (int *)malloc(size * sizeof(int))) == NULL)
        return -1; 

    //计数值清零
    for(i = 0; i < k; i++)
        counts[i] = 0;

    //对应数字 data[j]存入count数组count位置data[j],并在 其内容存放出现次数 
    for(j = 0; j < size; j++)
        counts[data[j]] =  counts[data[j]] + 1;

    //把计数次数装换为,对应数字该从哪个位置放,倒放   
    for(i = 1; i < k; i++)
        counts[i] = counts[i-1] + counts[i]; 

    for(j = size - 1; j >= 0; j--)
    {
        //counts[data[j]] - 1就是 data[j]该放的位置 
        temp[counts[data[j]] - 1] = data[j];
        counts[data[j]] = counts[data[j]] - 1;
    } 
    memcpy(data, temp, size * sizeof(int));
    free(counts);
    free(temp);

    return 0; 
}

3、基数排序
线性排序,先比较数据的最低位进行排序(每一位排序应用计数排序),然后次低位依次排序。
基数排序并不局限于对整型数据进行排序,只要能把元素分割成整形数就可以使用基数排序。
这里写图片描述

//p是位数,k是进制 
int rxsort(int *data, int size, int p, int k)
{
    int *counts, *temp;
    int index, pval;
    int i, j, n;

    if((counts = (int *)malloc(k * sizeof(int))) == NULL)
        return -1;
    if((temp = (int *)malloc(size * sizeof(int))) == NULL)
        return -1; 

    //对每一位进行排序 
    for(n = 0; n < p; n++)
    {
        for(i = 0 ;i < k;i++)
            counts[i] = 0;

        //对于整数则pval=k*n,当做取某位上数字时的除数 
        pval = (int)pow((double)k, (double)n); 
        for(j = 0;j < size; j++)
        {
            index = (int)(data[j] / pval) % k;
            counts[index] = counts[index] + 1;
        } 
        for(i = 1;i < k;i++)
        {
            counts[i] = counts[i] + counts[i - 1];
        }
        for(j = size - 1; j >= 0; j--)
        {
            index = (int)(data[j] / pval) % k;
            temp[counts[index] - 1] = data[j];
            counts[index] = counts[index] - 1;
        }

        memcpy(data, temp, size * sizeof(int));

        free(counts);
        free(temp);
        return 0;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值