计数排序
当输入元素是n个0到k之间的整数时,他的运行时间是O(n+k),计数排序不是比较排序,它快于任何比较算法。
用来计数的数组C的长度取决于排序数组的数据范围,如果数据范围很大,需要大量的数组。但是计数排序可以在基数排序的的算法范围来排序数据范围很大的数组。
步骤:
1、找到数据最小元素和最大元素
2、统计数组中每个值为i的元素出现的个数,存入计数数组第i项
3、对所有的计数累加
4、反向填充将每个元素i放在新数组的第C(i)项,每放一个元素就将C(i)减1
#define MAX_RANGE 100
void conting_sort(int *ini_arr,int *sorted_arr,int n) //所需空间为2*n+k
{
int* count_arr= new int[MAX_RANGE];
for(int i=0,i<MAX_RANGE;i++){
count_arr[i] =0;//初始化数组元素为0
}
//统计数组中每个元素出现的个数
for(int j =0;j<n;j++){
count_arr[ini_arr[i]]++;
}
//统计数组,每项存前N项和,这实质为排序过程
for(int k=1;k<MAX_RANGE;k++){
count_arr[k]+=count_arr[k-1];
}
//将计数排序结果转化为数组元素的真实排序结果
for(int j =n-1;j>=0;j--){
int elem =int_arr[j] //取待排序元素
int index = count_arr[elem]-1//待排序元素在有序数组中的序号
sorted_arr[index]= elem; //将待排序元素存入结果数组中
count_arr[elem]--; //修正排序结果,其实是针对算的元素的修正
}
free(count_arr);
}
桶排序
桶排序是计数排序的变种,把计数排序中相邻的m个小桶放到一个大桶中,对每个桶进行排序,然后合并成最后结果。
基本思想:
桶排序假设序列由一个随机过程产生,该过程将元素均匀而独立的分布在(0,1)上,我们把(0,1)区间划分成n个大小相同的子区间,将n个记录分布到桶中,如果一个桶中有多个记录,则需要在桶中进行排序,然后依次把桶中记录的列出来得到有序序列。
效率分析:
时间复杂度为线性的O(N+C),C
为桶内快排的时间复杂度。如果相对于同样的N,桶的数量M越大,其效率越高,最好的时间复杂度为O(N)。空间复杂度为O(N+M),桶排序是稳定的。
3 、基数排序
基本思想:
将待排数据中每组关键字依次进行桶分配。
具体事例:
278,109,063,930,589,184,505,269,008,083
我们分别将每个数值的个位,十位,百位分成三个关键字。
然后从最低个位开始,对所有数据的个位进行桶分配,每个数字都是0~9,因此桶的大小为10
依次得到下列序列:
930,063,083,184,505,278,008,109,589,269
对十位进行桶分配,输出序列:
505,008,109,930,063,269,278,083,184,589
对百位进行桶分配
008、063、083、109、184、269、278、505、589、930
效率分析:
基数排序的性能比桶排序要略差。每一次关键字的桶分配都需要O(N)的时间复杂度,而且分配之后得到新的关键字序列又需要O(N)的时间复杂度。假如待排数据可以分为d个关键字,则基数排序的时间复杂度将是O(d*2N) ,当然d要远远小于N,因此基本上还是线性级别的。基数排序的空间复杂度为O(N+M),其中M为桶的数量。一般来说N>>M,因此额外空间需要大概N个左右。
但是,对比桶排序,基数排序每次需要的桶的数量并不多。而且基数排序几乎不需要任何“比较”操作,而桶排序在桶相对较少的情况下,桶内多个数据必须进行基于比较操作的排序。因此,在实际应用中,基数排序的应用范围更加广泛。