下面谈到的两类排序是基于非交换排序的算法,时间复杂度都是线性的,前提必须是正整数。最下方有动画演示,便于我们理解这两个算法。
计数排序,顾名思义就是对数组中的元素进行计数的方式进行排序。利用一个辅助数组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;
}