排序(三):计数排序,基数排序

下面谈到的两类排序是基于非交换排序的算法,时间复杂度都是线性的,前提必须是正整数。最下方有动画演示,便于我们理解这两个算法。

计数排序

计数排序,顾名思义就是对数组中的元素进行计数的方式进行排序。利用一个辅助数组count对原数组的每一位数值存储到辅助数组的下标位置。来看一个列子
5, 5, 3, 100, 4 9, 100 , 3, 2, 3
存储到辅助数组的情况为:
count[2]=1;
count[3]=3;
count[4]=1;
count[5]=2;
count[9]=1;
count[100]=2;
count[i]=0的这里就没列举了。
然后依次输出这些数2,3,3,3,4,5,5,9,100,100
当然要输出这样的序列肯定是要建立映射关系的,count数组是对原数组出现的数字计数,也利用这一特点,可以将每一个数出现在第几个位置求出来,count[2]就是第1个数,count[3]就是第2到第4个数。。。依次类推,求出count[i]的前i项和。然后每个位置-1(下标从0开始),就是排序好的位置。
现在来想想如果这些数是
18921,2384,2323,23213,31223,4545,6577
我们也开辟一个31223的空间来计数吗?
答:既然是建立一个映射关系,我们只需控制到最小和最大这个区间里计数即可,因为在其区间外的count[i]都是0,对结果没有任何影响。

优缺点和性能

优点:对区间[mi,mx]小的正整数排序很快
缺点:限制条件太多,正整数不能超过1e8次方,必须是正整数
性能:时间复杂度都是O(n+k),k是区间的长度,空间复杂度为O(k);

代码

#include
#include 
using namespace std;
const int MAXN=1e5;
int arr[10]={5,5,3,100,4,9,100,3,2,3},ans[MAXN];
void countSort(int n)
{
	//第一步:求出数组可控范围
	int mx=0,mi=(1<<30);
	for(int i=0;i<n;i++)
	{
		mx=max(mx,arr[i]);
		mi=min(mi,arr[i]);
	}
	//第二步:定义一个计数数组,标记数组中元素出现的次数, 建立映射关系 
	int MaxNum=mx-mi+1;
	int count[MaxNum];
	for(int i=0;i<MaxNum;i++)	count[i]=0;//初始化 
	for(int i=0;i<n;i++)	count[arr[i]-mi]++;//对数组计数 
	for(int i=1;i<MaxNum;i++)	count[i]+=count[i-1];//建立映射关系 
	//第三步:将原数组拷贝到输出数组中 
	for(int i=n-1;i>=0;i--)
	{
		int pos=count[arr[i]-mi];//arr[i]在排序后中的位置 
		ans[pos-1]=arr[i];//数组下标从0开始 
		count[arr[i]-mi]--;//当前位置减一,为重复整数考虑到的这一点 
	} 
}
int main()
{
	int n=10;
	countSort(n);
	for(int i=0;i<n;i++)	printf("%d ",ans[i]); 
	return 0;
}

基数排序

基数排序,是一种根据一组数据中的每一位来进行的排序。基数排序利用了计数排序的思想,第一趟排个位,第二位排十位。。。最后一趟排最高位
下面来说一个例子。
134,234,423,458,873,590,328
排个位:590,423,873,134,234,458,328
排十位:423,328,134,234,458,873,590
排百位:134,234,328,423,458,590,873
假设每个数的位数不同呢?
答:其实是一样的,如果某个数的位数排完了,那么他就是有序的了,压入到count中的数是0.

优缺点和性能

优点:数组中的正整数变化很大时,这个算法的优点就体现出来了。
缺点:只能是正整数
性能:空间复杂度O(n),时间复杂度为O(n).

代码

#include
const int MAXN=1e5;
int arr[10]={18921,2384,2323,23213,31223,4545,6577,56443,23123,12986},ans[MAXN];
void radixSort(int n,int key)
{
	int count[10];//对每个位数出现0-9的次数 
	for(int k=1;k<=key;k*=10)//枚举数组中的每位数 
	{
		for(int i=0;i<10;i++)	count[i]=0;
		for(int i=0;i<n;i++)	count[arr[i]/k%10]++;
		for(int i=1;i<10;i++)	count[i]+=count[i-1];
		for(int i=n-1;i>=0;i--)
		{
			int pos=arr[i]/k%10;
			ans[count[pos]-1]=arr[i];
			count[pos]--;
		}
		for(int i=0;i<n;i++)	arr[i]=ans[i];
	}
}
int main()
{
	int key=1,n=10;
	for(int i=0;i<n;i++)
		while(key<arr[i])	key*=10;//多乘了一次 
	radixSort(n,key/10);
	for(int i=0;i<n;i++)	printf("%d ",arr[i]);
	return 0;
}

计数排序:

基数排序
排序(三):计数排序,基数排序_第1张图片

你可能感兴趣的:(算法与数据结构)