0 前言
常见的非比较排序算法:计数排序,基数排序,桶排序;平均时间复杂度都是O(n),但是限制比较多
比较算法的时间复杂度下限为O(nlogn)
1 思路
输入的元素使n个0到k之间的整数时,时间复杂度为 Θ(n+k)
由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组需要大量时间和内存。但计数排序可用于在基数排序算法中,来排序数据范围很大的数组。
通俗理解:10个年龄不同的人,统计出8个人的年龄比A小,那么A的年龄排在第9位。年龄重复时,需要特殊处理(保证稳定性),所以最后要反向填充目标数组,以及将每个数字的统计减去1。
算法步骤如下:
- 找出待排序的数组中的最大和最小的元素
- 统计数组中每个值为i的元素出现的次数,存入数组C的第i项
- 对所有的计数累加(从C中的第1个元素开始,每一项和前一项相加)
反向填充目标数组,将每个元素i放在新数组的第C(i)项,每放一个就将C(i)减去1
假定输入时数组A[1..n], length(A)=n。 另外还需要一个存放排序结果的数组B[1..n],以及提供临时存储区的C[0..k] k是所有元素中最大的一个。
#include <iostream>
using namespace std;
int getMax(int arr[], const int size){
int max = 0;
for(int i = 0; i < size; ++i){
if(arr[i] > max) max = arr[i];
}
return max;
}
void countingSort(int a[], int size_a, int b[], const int k){
int* c = new int[k+1];
for(int i = 0; i < k; ++i){
c[i] = 0; // 初始化计数数组
}
for(int i = 0; i < size_a; ++i){
c[a[i]]++; // 计数
}
for(int i = 1; i <= k; ++i){
c[i] += c[i-1]; // 累加
}
for(int i = size_a - 1; i >= 0; --i){ // 反向填充
b[c[a[i]] - 1] = a[i]; // 对应位置
c[a[i]]--; // 填充完成一个删一个
}
}
int main(){
int a[] = {2,5,3,0,2,3,0,3};
const int size_a = sizeof(a)/sizeof(int);
int* b = new int[size_a];
countingSort(a, size_a, b, getMax(a, size_a));
for(int i = 0; i < size_a; ++i){
cout << b[i] << " ";
}
return 0;
}