计数排序(C语言实现)------学习笔记

计数排序百度百科介绍:

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

算法思想

我们先来回顾一下经典的排序算法,无论是冒泡排序,归并排序,还是快速排序等等,都是基于元素之间的比较来进行排序的。但是有一种特殊的排序算法叫计数排序。

这种排序算法不是基于元素比较,而是额外申请一个数组空间,利用数组下标来确定元素的正确位置,数组中存储元素出现的次数。 所以我们关键在于构建数组下标和元素的映射关系(函数关系)。之后按照数组从小到大(或从大到小)输出。

计数排序的核心思想:用额外开辟的数组空间存储输入的数据值出现的次数。

作为一种线性时间复杂度的排序,计数排序要求输入的数据必须是有确定范围的整数。

算法过程

1.声明一个数组A,用来存储整数序列L。

2.用O (n)的时间扫描一下整个数组 A,获取最小值 min 和最大值
max。

3.根据待排序集合中最大元素max和最小元素min的差值范围,申请额外空间。即定义一个新的数组B[max-min+1]用来存储元素出现的次数。

4.构建数组A中元素与数组B下标值的映射关系。Index=F(data)

5.统计数组A元素中出现的次数,记录在相应的数组B中。

6.输出目标整数序列L,具体操作:遍历数组B,输出元素及其对应次数。

优点:速度很快

在确定范围的整数进行排序,排序速度优于其他排序,时间复杂度O(n+k)。(k为额外空间的大小,n为输入的数据个数)

缺点:

具有两大局限性:

1. 必须为确定范围的整数的排序。

当不是整数时,如0.234,0.00001无法建立数组下标与元素的关系。

2. 无用空间较大时不适用

即(k-n)/k很大时,并不适用于计数排序。比如给定20个随机整数,范围在1到10000000之间,此时如果使用计数排序的话,就需要创建长度为1千万的数组去存储20个数,出现大量空白空间。不但严重浪费了空间,而且时间复杂度也随之升高。

正是由于这两大局限性,才使得计数排序不像快速排序、归并排序那样被人们广泛适用

下面我们来解决学校oj里一个相应的题目。

题目描述

给你n个整数,请按从大到小的顺序输出其中前m大的数。

Input:

每组测试数据有两行,第一行有两个数n,m(0<n,m<1000000),第二行包含n个各不相同,且都处于区间[-500000,500000]的整数。

Output:

对每组测试数据按从大到小的顺序输出前m大的数。

Sample Input:

5 2

-207693 190069 13569 -48891 -354975

20 6

-339148 446412 -322565 39597 -139187 108119
159993 218397 201075 174963 -149825

137785 105930 -55557 -363362 268859 -139719
-425420 -269331 -333651

Sample Output:

190069 13569

446412 268859 218397 201075 174963 159993

题目分析:

第一行先读入n和m的值。第二行读取n个各不相同的数。此时每一组的读取工作完毕。

然后按照从大到小排序依次输出,共输出前m个。

发现此题目主要考察的是排序的算法。

我们可以根据题目特点,选择相应的排序方法。

此题目特点为:数据量大在百万级别,但数据为可以确定范围的整数。

因此我们选择计数排序,对于此题目,它的排序速度足够的快。

算法描述:

我们按照算法过程进行解题。

  1. 由于题目给了数据值的范围,所以我们可以将第一步和第二步省略。直接操作第三步 申请一个计数数组 arr 并初始化为 0,用来统计输入数据值出现的次数,大小为{max- min+1}即声明 int arr[1000000+1]。为什么要+1 呢,因为数组大小为数据值可能出现的个 数,算一下从-500000 到 500000 共有多少个整数。
  2. 构建输入数据值t与计数数组arr[index]下标的函数关系,可以得出index=t+500000 即 arr[0]统计-500000 出现的次数,a[500000]统计 0 出现的次数,a[500021]统计 21 出现 的次数。
  3. 读取数据值t,每读取一个数,使arr[t+500000]++。
  4. 因为题目从大到小输出,所以我们逆序遍历数组arr,输出数据t,t为index(数组下 标值)-500000 。因为数据值互不相同,最多记录一次,所以判断 arr[index]==1 输出 即可。共输出 m 个,输出一个值 m–。

此题目没有说明要读取多少组数据,那如何读取若各组数据呢?
用一个 while 循环,判断条件为 scanf 是否成功读取 n,m 的值,如果成功读取 n,m 的值那 么 scanf 函数的返回值为 2。成功读取一个返回值为 1。未成功读取,返回值为 0。
scanf 函数返回成功读入的数据项数,读入数据时遇到了“文件结束”则返回 EOF。
例: while((scanf(“%d %d”,&n,&m))==2) {

}

C语言源代码:

#include<stdio.h>
int arr[1000000+1]={0};   //开辟大小为max-min+1的计数数组arr
void main()
{
    int n,m,i,t;                   //t用来读取数据值
    while(scanf("%d %d", &n,&m)==2)   //读取若干组数据
    {
        getchar();                         //读取换行符,多用于实际程序,解题中可省略,
        for (i = 0; i < 1000001; i++) //每组数据测试前初始化
        {
            arr[i]=0;
        }
        for (i = 1; i <= n; i++)     
         {
            scanf("%d", &t);         //将数据读入t
            arr[t + 500000]++;    //统计数据t出现的次数
        }
        for (i = 1000000; i >= 0 && m> 0; i--)
        {
            if (arr[i] == 1)          //被记录一次就输出
            {
                printf("%d ", i- 500000);
                m--;                    //输出m个
            }
        }
    }
}

总结

读完此篇文章,我们至少要明白和记住这几点
1.计数排序的核心思想 2.计数排序的适用条件3.计数排序的算法过程

遇到题目,首先判断是否适合计数排序,判断条件:可以确定范围的整数序列。 然后按照算法过程,写出大体的算法代码;分析题目,根据题目特点进行修改代码,使代码简洁、完善。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值