zoukankan      html  css  js  c++  java
  • 算法:计数排序

    计数排序和鸽巢排序十分相似,同属于桶排序的一种,它的优势在于在对一定范围内的整数排序时,它的速度极快无比,但空间上就恶心一些了。

    例题

    洛谷1177 排序

    题目描述
    将读入的 N 个数从小到大排序后输出。

    输入格式
    第 1 行为一个正整数 N。
    第 2 行包含 N 个空格隔开的正整数 a[i],为你需要进行排序的数,数据保证了a[i]不超过10^9。

    输出格式
    将给定的 N个数从小到大输出,数之间用空格隔开。

    输入输出样例
    输入

    5
    4 2 4 5 1
    

    输出

    1 2 4 4 5
    

    说明提示
    对于20% 的数据,有 N <= 10^3。
    对于100% 的数据,有 N <=10^5 。

    备注
    本来洛谷1177是需要用快速排序来求解的,但实在找不到用计数排序就能过的裸题,因此只好以这道题为例子了。这道题用计数排序实际上只能拿60分,因为计数排序对空间的需求量较大。

    计数排序

    计数排序,顾名思义就是数每个数出现的次数,这里用cnt数组来记录a数组中每个数出现的次数,cnt[i]表示i这个数在a数组中出现的次数,而根据a数组计算得出这个cnt数组是极为简单的。但考虑到负数情况,我们选择对数组进行平移,这里我们取a数组的最小值对应到0,既cnt[i]表示(i + a_min)这个数在a数组中出现的次数。

    我们发现此时这个cnt数组跟鸽巢排序的nest数组是完全一样的,但是往后就不同了。

    接下来我们要改变cnt数组的含义了,使cnt[i]表示a数组中小于等于a[i + a_min]的数的个数,这个是可以利用当前cnt数组中每个位置所对应的数在a数组中出现的次数算出来的,既cnt_new[i] = cnt_old[i] + cnt_new[i - 1],就是cnt[i] += cnt[i - 1],算个前缀和而已。

    最后就是通过cnt数组将a数组排序了,这里考虑到如果直接在a数组上面改动会有后效性,所以新建一个b数组暂时储存。之后因为cnt[a[i] - a_min]表示小于等于a[i]的数的个数,就是a[i]应该是a数组中第几小的,你也可以认为是a[i]应该排在第几位,所以让b数组对应的位置等于a[i],既让b[cnt[a[i] - a_min]] = a[i]。可是如果后面一次有一个与a[i]相等的a[j],那么这个a[j]不就被跳过去了吗?所以这里每次要将cnt[a[i] - a_min]--。这样这样再把b数组的值给回a数组就大功告成了。

    最后,算一下时间复杂度:这里我们设m为最大值与最小值之差,那么时间复杂度就是O(n + m)。这种排序算法适合应用在n特别大,但m较小的情况。如果m太大,空间、时间都得炸。例如此题的前两个测试点,就因为m太大,所以就RE了。

    代码

    # include <cstdio>
    # include <cmath>
    # include <cstring>
    # include <algorithm>
    # include <climits>
    
    using namespace std;
    
    const int N_MAX = 100000, M_MAX = 100000000;
    
    int n;
    int a[N_MAX + 10], b[N_MAX + 10];
    int cnt[M_MAX + 10];
    
    void countSort()
    {
    	int a_max = INT_MIN, a_min = INT_MAX;
    	for (int i = 1; i <= n; i++) {
    		a_min = min(a_min, a[i]);
    		a_max = max(a_max, a[i]);
    	}
    	for (int i = 1; i <= n; i++)
    		cnt[a[i] - a_min]++;
    	for (int i = 1; i <= a_max - a_min; i++)
    		cnt[i] += cnt[i - 1];
    	for (int i = 1; i <= n; i++)
    		b[cnt[a[i] - a_min]--] = a[i];
    	memcpy(a, b, sizeof(b));
    }
    
    int main()
    {
    	scanf("%d", &n);
    	for (int i = 1; i <= n; i++)
    		scanf("%d", &a[i]);
    	countSort();
    	for (int i = 1; i <= n; i++)
    		printf("%d ", a[i]);
    	printf("
    ");
    	return 0;
    }
    
  • 相关阅读:
    Android.02.Button
    Android.01. TextView
    Android.00.IDEA中Android开发环境搭建
    Java34.Marven
    CSS 滚动条样式
    不常用的HTML和CSS 记录
    JS 规则
    svg转化成canvas以便生成base64位的图片
    ArcGIS Server 10.1 安装教程
    ArcGis API JS 4.X 本地化部署
  • 原文地址:https://www.cnblogs.com/000zwx000/p/12330701.html
Copyright © 2011-2022 走看看