计数排序-C语言实现

排序可以通过比较实现,也可以通过不比较实现。比较排序就是指通过比较操作(通常是“小于或等于”操作)来确定两个元素中哪个应该放在序列前面。比较排序算法理论的算法复杂度下限为:O(nlgn),也就是说比较排序的时间复杂度不可能比O(nlgn)更小了。而非比较的排序,如计数排序桶排序计数排序,则可以突破O(nlgn)的时间下限。但要注意的是,非比较排序算法的使用都是有条件限制的,例如计数排序,对数据范围过大的元素进行排序需要耗费大量的内存和时间,但对数据范围较小的大量数据排序,计数排序却可以发挥奇妙的作用。下面将重点介绍这个算法:

(1)什么是计数排序

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围,n是待排序元素的个数),快于任何比较排序算法。 当然这是一种牺牲空间换取时间的做法,而且当O(k)>O(n*log(n))的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(n*log(n)), 如归并排序,堆排序)。这个要记住:计数排序的适用场合。

(2)计数排序算法思想

计数排序的基本思想是对于给定的输入序列中的每一个元素x,确定该序列中值小于x的元素的个数(此处并非比较各元素的大小,而是通过对元素值的计数和计数值的累加来确定)。一旦有了这个信息,就可以将x直接存放到最终的输出序列的正确位置上。例如,如果输入序列中只有17个元素的值小于x的值,则x可以直接存放在输出序列的第18个位置上。当然,如果有多个元素具有相同的值时,我们不能将这些元素放在输出序列的同一个位置上,有重复时需要特殊处理(保证稳定性),需要在最后反向填充目标数组,并将每个数字的统计减去1。

(3)计数排序算法具体步骤

1.找出待排序的数组中最大和最小的元素
2.统计数组中每个值为i的元素出现的次数,存入数组C的第
i项,即扫描序列A,以A中每个元素的值为索引,将C中对应位置的次数统计加1,C[i]为A中i出现的次数。
3.对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)以确定值为i的元素在数组中出现的位置。
4.反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1
假定输入是个数组A[1…n], length[A]=n。 另外还需要一个存放排序结果的数组B[1…n],以及提供临时存储区的C[0…k](k是所有元素中最大的一个)例如:

假设数字范围在 0 到 9. A[]={0, 4, 1,2, 7,1, 5, 2,7,9}
  1) 使用一个数组c记录每个数组出现的次数
  数组索引(下标):      0  1  2  3  4  5  6  7  8  9
  数组c:                      1  2  2  0   1  1  0  2  0  1
  2) 累加所有计数(从C中的第一个元素开始,每一项和前一项相加)

 数组索引(下标):     0  1  2  3  4  5  6  7  8  9

 数组c:                       1  3  5  5  6  7  7  9  9  10

更改过的计数数组就表示每个元素在输出数组中的位置
  3) 反向填充目标数组:将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减去1

例如对于:0, 4, 1, 2, 7, 5, 2,7,9中0的位置是1,则把0放在输出数组的第一个位置,并将计数减一。1出现的位置是3,就将3放在输出数组的第3个位置,并将计数减一,下一个1出现的时候就放在第2个位置了.(方向可以保持稳定)

(4)计数排序具体C语言实现

#include <stdio.h>
#include <stdlib.h>
#define random(x) rand()%(x)
#define NUM 100     // 产生100个随机数
#define MAXNUM 200     //待排序的数字范围是0-200
void countingSort(int A[], int n, int k){
    int *c, *b;
    int i;
    c = (int *)malloc(sizeof(int)*k);/*临时数组,注意它的大小是待排序序列中值最大的那个。如假定该排序序列中最大值为1000000,则该数组需要1000000*sizeof(int)个存储单元*/
    b = (int *)malloc(sizeof(int)*n);  /*存放排序结果的数组*/
    for (i = 0; i < k; i++)
        c[i] = 0;                       /*初始化*/
    for (i = 0; i < n; i++)
        c[A[i]] += 1;                   /*统计数组A中每个值为i的元素出现的次数*/
    for (i = 1; i < k; i++)
       c[i] = c[i - 1] + c[i];         /*确定值为i的元素在数组c中出现的位置*/
    for (i = n - 1; i >= 0; i--)
    {
        b[c[A[i]] - 1] = A[i];       /*对A数组,从后向前确定每个元素所在的最终位置;*/
        c[A[i]] -= 1;
    }
    for (i = 0; i < n; i++)
        A[i] = b[i];                /*这个目的是返回A数组作为有序序列*/
    free(c);
    free(b);
}
void printArray(int A[], int n){
    int i = 0;
    for (i = 0; i < n; i++){
        printf("%4d", A[i]);
    }
    printf("\n");
}
/*测试*/
int main()
{
    int A[NUM];
    int i;
    for (i = 0; i < NUM; i++)
        A[i] = random(MAXNUM);
    printf("before sorting:\n");
    printArray(A, NUM);
    countingSort(A, NUM, MAXNUM);
    printf("after sorting:\n");
    printArray(A, NUM);
    return 0;
}

时间复杂度: O(n+k)
空间复杂度: O(n+k)
(5)其它
1. 计数排序是有效的,如果输入数据的范围是不显著大于数字的个数
2. 它不是一个基于比较的排序。它运行的时间复杂度为O(n)
3. 它经常被用来作为另一个排序算法像基数排序的一个子程序。
4. 计数排序可以扩展到负输入也可以。

主要参考:http://www.cnblogs.com/ttltry-air/archive/2012/08/04/2623302.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值