计数排序(Counting sort)
大家都知道的是,基于比较的排序算法的时间复杂度的下界是 O(n log(n))。这一结论是可以证明的,所以在基于比较的算法中是找不到时间复杂度为 O(n)的算法的。这时候,非基于比较的算法,如计数排序、基数排序和桶排序,是可以突破这个下界的。但是,非基于比较的排序的使用限制却是较多的,如计数排序仅能对较小整数进行排序,且要求排序的数据的规模不能过大;基数排序可以对长整数进行排序,但是不适用于浮点数;桶排序可以对浮点数进行排序。
计数排序(Counting sort)是一种稳定的排序算法。计数排序是最简单的特例,由于用来计数的数组C的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存,适用性不高。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。但是,计数排序可以用在基数排序中的算法来排序数据范围很大的数组。当输入的元素是 n 个 0 到 k 之间的整数时,它的运行时间是 Θ(n + k)。
假定输入是个数组A【1...n】, length【A】=n。 另外还需要一个存放排序结果的数组B【1...n】,以及提供临时存储区的C【0...k】(k是所有元素中最大的一个)。算法伪代码:
算法的步骤如下:
- 找出待排序的数组中最大和最小的元素
- 统计数组中每个值为t的元素出现的次数,存入数组C的第t项
- 对所有的计数累加(从C中的第一个元素开始,每一项和前一项相加)
- 反向填充目标数组:将每个元素t放在新数组的第C(t)项,每放一个元素就将C(t)减去1
算法实现:
#include <iostream> #include <ctime> #include <algorithm> #define NUM_MAX 10 using namespace std; void myswap(int &a, int &b); void CountSort(int *p, int n); int main(void) { int arr[NUM_MAX]; srand((unsigned)time(NULL)); for(int i = 0; i < NUM_MAX; i++) { arr[i] = rand()%100 + 1; cout<<arr[i]<<" "; } cout <<endl; CountSort(arr, NUM_MAX); for(int i = 0; i < NUM_MAX; i++) { cout<<arr[i]<<" "; } cout<<endl; return 0; } void myswap(int &a, int &b) { /* int tmp = a; a = b; b = tmp;*/ //以下这两种方法时,要考虑不能相等才进行交换,否则出错 /* f(a != b) { a = a + b; b = a - b; a = a - b; } */ if(a != b) { a = a ^ b; b = a ^ b; a = a ^ b; } } void CountSort(int *p, int n) { int max_val = *max_element(p, p+n);//求数组最大值 int *c = new int[max_val+1]; // 开辟一个临时数组 int *tmp = new int[n];// 开辟一个保存结果的临时数组 for(int i = 0; i <= max_val; i++) c[i] = 0; // 使count[i]等于等于i的元素的个数 for(int i = 0; i < n; i++) c[ p[i] ]++; for(int j = 1; j <= max_val; j++) c[j] += c[j-1]; for(int i = 0; i < n; i++) { --c[ p[i]];// 因为数组下标从0开始,所以这个放在前面 // 这个比较绕, count[input[i]-1] 就代表小于等于元素input[i]的元素个数,就是input[i]在result的位置 tmp[ c[ p[i]]] = p[i]; } copy(tmp, tmp+n, p);// 调用copy函数把结果存回原数组 delete [] c; delete [] tmp; }
结果:
这个实现对书上给的伪代码稍微改了一点, 如果计数排序每次要自己另外开一个数组保存结果才能用,感觉肯定很不爽。 所以在计数排序里面,把结果在拷贝到原来的数组。这样用的时候不用自己开数组,方便多了
我这里有一个更简单的" 计数排序 ",也可以实现排序。但是这个却又不太像计数排序。到底这个算不算是计数排序呢? 这个问题也困扰了我很长时间,终于在某一天让我给发现了
。 这个再后面再讲。 先把这个取名为“计数排序特殊版”
代码:
void CountSort(int *p, int n) { int max_val = *max_element(p, p+n);//求数组最大值 int *c = new int[max_val+1]; // 开辟一个临时数组 for(int i = 0; i <= max_val; i++) c[i] = 0; // 使count[i]等于等于i的元素的个数 for(int i = 0; i < n; i++) c[ p[i] ]++; int j = 0; for (i = 0; i <= max_val; i++) { while (c[i]-- > 0) { ar[j++] = i; } } delete [] c; }
“计数排序特殊版”:其实那是一个“特殊的桶排序”,一共有k个桶,然后把每个元素都放到对应的那个桶中。和一般的桶排序不同,这里每个桶放的都是相同的元素,所以最后不需要再用另外一个排序算法给每个桶再排序,直接把所有的桶合并在一起就是最终的排序了!



浙公网安备 33010602011771号