计数排序

  前面提到的排序(选择排序、插入排序、归并排序、快速排序、堆排序等)都是比较排序。也就是说,以上排序实现的思路都是需要元素的比较才能实现。换一种思路,是不是元素排序必须需要数组内的元素之间的比较呢?其实不然,今天介绍的计数排序就不是利用比较,而是巧妙的利用了下标和元素的关系进行排序,把时间转换成空间,典型的时空变换思路。

  在这个思想中,需要维护三个数组a[],b[],c[],数组a是原始的输入数组,数组b是排序后需要输出的数组,数组c是计数数组。这就是所需要的空间。

  首先,初始化计数数组c。那么到底我们需要维护一个多大的计数数组呢?这就要求我们必须知道原始输入数组中的最大值。开辟一个大小为原始输入的最大值的数组,并且c中每个元素都初始化为0。为什么要这么做呢。因为数组c的下标即为原始值,每个下标所对应的数值即为此数值的个数。例如c[5] = 10,就表示a中有10个5。

  其次,就是对数组c中的数值进行修改。即c[a[i]]++。循环a中的所有元素,把每个c[a[i]]自增1。这个不难理解。就是统计每个元素的个数。

  然后就是关键的第一步,c[i] = c[i] + c[i-1]。这句代码的意思就是,比原始值i小的数值个数是c[i]+ c[i-1]。这个开始有点绕,不妨这样想一下:c中第i个数就是原始i的个数,前面所有的(0到i-1)都是比i小的数,把他们都加起来就是所有比i小的数值的个数。这句话就是完成这么一个功能。为什么要实现这么一个功能呢?接下来就是关键的第二步,排序。

  最后这一步排序其实已经很明白了。我已经知道比我小的数的个数了,那我就应该直接放在那些个数的后面一个不就完了?确实是这样。也就是b[c[a[i]] - 1] = a[i](考虑到了数组下标为0所以减1)。正因为如此,简化了时间复杂度。这样其实还没有完,还有一个十分重要的步骤,那就是把所对应的c中的数值(计数个数)减一,因为你已经把一个 i 放进b里面了。

  具体C++代码如下,已经在CB完美运行。

 1 #include <iostream>
 2 using namespace std;
 3 
 4 int c[100];
 5 void countingSort(int a[], int b[], int max, int len) {
 6     for(int i = 0; i <= max; ++i){
 7         c[i] = 0;
 8     }
 9 
10     for(int i = 0; i != len; ++i){
11         c[a[i]] = c[a[i]] + 1;
12     }
13 
14     for(int i = 1; i <= max; ++i){
15         c[i] = c[i] + c[i - 1];
16         cout<<c[i]<<endl;
17     }
18 
19     for(int i = len-1; i >= 0; --i){
20         b[c[a[i]] - 1] = a[i];
21         c[a[i]] = c[a[i]] - 1;
22     }
23 }
24 
25 int main() {
26     int a[8] = {2,5,3,0,2,3,0,3};
27     int b[8];
28     int max = 5;
29     int len = sizeof(a)/sizeof(int);
30     countingSort(a, b, max, len);
31 
32     for(int i = 0; i < 8; ++i)
33         cout<<b[i]<<" ";
34 
35     return 0;
36 }

 

posted @ 2016-01-28 14:23  VF_Ever  阅读(280)  评论(1编辑  收藏  举报