计数排序与map的一些使用(c、c++描述)

计数排序与MAP

计数排序

先用一个
例题1:

input:输入n,第二行输入n个学生的成绩,成绩范围[0,100]
output:对这n份成绩排序

在这个问题上稳定性都不需要考虑,随便一种排序都能解决,这里使用一种非比较排序,计排。
上代码,C++描述

#include<bits/stdc++.h>
using namespace std;
int m[100]={
    
    0};
int main()
{
    
    int n=0,v=0,a=0,i=0,j=0,k=0;
scanf("%d",&n);
for(i=0;i<n;i++)
{
    
    
    scanf("%d",&a);//输入每个成绩
    m[a]++;//将每个值出现次数存入m数组,下标为该值
}
for(i=0;i<=100;i++)
{
    
    
    for(j=0;j<m[i];j++)
        printf("%d ",i);//按大小顺序以及出现次数输出
}
    return 0;
}

先看时间复杂度,由数据范围和分布情况所决定。如上,在数据分布均匀且大小合适的情况下甚至比其他任何排序更快。但数据变得更广更复杂时,时间、空间的浪费就显得很大。在正常情况下,需要开辅助空间,但上述代码中,并不需要。
由此看出,计数的使用条件:数据取值范围小,但数据量较大;

例题2:

input:一个由字母组成的字符串,长度1000以内
output:对字符串内字母按字典序排序后输出

上代码,C描述

#include<stdio.h>
#include<string.h>
int main()
{
    
    char s[1000];
int i=0,j=0,l=0,a[128]={
    
    0};
    gets(s);//实际上不建议使用这种输入方式
for(i=0;i<strlen(s);i++)
a[s[i]]++;//虽然开了辅助空间但长度极小
for(i=0;i<128;i++)
for(j=0;j<a[i];j++)
      printf("%c ",i);

    return 0;
}

在面对这种情况时,已知按ascii范围不会超过128,计数排序的效果就极其突出;
可以看到,当数据的范围很小时,计数排序有时候有着优秀的效果,在查找第n项,以及去重查重等问题也有发挥空间,在代码方面也简洁明了。
但是当数据范围庞大时,上述的方法的时、空间要求也在增加,如何避免浪费是我们首先可以考虑的优化方向,这里我们就引入了map。

C++ STL MAP

map的定义与用法可以自行搜索,这里只描述一些操作;
我们假设单个成绩取值范围是0-10^9;n与m<=100000;

input:输入n,输入m,第二行输入n个学生的成绩
第3行输入m个数,表示询问这些成绩的编号,从 1 开始编号。
output:输出该成绩对应的第一次出现时的编号

首先,这是一个可用计数数组进行查找的问题,但如果直接去开数组,10^9的大小会直接sizetoolarge,
但如果我们去开map,就会给出足够的空间;

int n,m;
map<int,int> f;//声明
int main(){
    
    
int a=0,i=0,j=0,n=0,m=0;
	scanf("%d %d",&n,&m);
	for(i=1;i<=n;i++){
    
    
		scanf("%d",&a);
		if(!f[a]){
    
    
			f[a]=i;//相同元素只记录第一次出现时序号
		}
	}
	for(i=1;i<=m;i++){
    
    
		scanf("%d",&j);
		if(!f[j]) 
		printf("-1 ");//若不存在
		else 
		printf("%d ",f[j]);
	}
	return 0;
}

我们用map<int,int>,来维护一个元素对,第一个是键类型(key),第二个是值类型(value)。本题中,已知10 ^ 5个数不可能覆盖0-10 ^ 9这个值域,那么我们在入桶时数组中必然有大量的空间浪费,但map所提供的键值对储存方法完美的解决了这个问题,我们不再需要长度为单个数据范围(maxV-minV)的数组如果只是搜索元素,时间、空间完全足够(实际上在上题中在输入流就已经完成了操作)。
实际上计数排序是桶排的一种最基础情况,作为非比较排序,使用范围终归不是很大,但其思路在很多情况下都有改良并发挥特效的空间。

猜你喜欢

转载自blog.csdn.net/weixin_43736127/article/details/109610964