算法导论第三版--计数,基数排序

计数,基数的中文读音都一样,这翻译的人还嫌我们计算机不够乱,真的想吐槽。

不管了,毕竟代码还是不一样的。

1、计数排序(counter sort):

      通过一个上限来统计集合里的数值(或者其他非数值类型映射的数值),并累计比小于自己(包括)的数值的统计的个数,从而形成排序的索引(也就是前面有多少个小于我的,我的位置就确定了)。

普通计数排序代码:(仍需优化,valuetype默认是整数类型)

 1 template<typename _InIt>
 2 void counter_sort(_InIt first, _InIt last, int k, _InIt result) {
 3     typedef typename iterator_traits<_InIt>::value_type _ValueType;
 4 
 5     vector<_ValueType> v(k, _ValueType(0));
 6     for(_InIt it=first; it!=last; ++it) v[*it]++;
 7 
 8     typename vector<_ValueType>::iterator itx = v.begin()+1;
 9     for(; itx!=v.end() ; ++itx) (*itx) += *(itx-1);
10 
11     for (int i = last - first -1; i>=0; i--) {
12         _ValueType val = *(first+i);
13         *(result + v[val] - 1) = val;
14         v[val]--;
15     }
16 }

2、基数排序(radix sort)

      基数排序道理挺容易理解,算法导论里有图解。不过看到有关ACM的一篇文章里提到MSD(Most Significant Dight)LSD(Least Significant Dight)排序,也就是说从那边开始排序效率高,举个整数的例子,3位数,假如每个数最高位都不重复,那肯定是从数值的最高位排一次就排好序了。不过实际中太多数可能不是这种情况,所以这个只能说跟那个大端小端一样,从那边开始根据具体情况来看。

下面是代码,修改一下计数排序来适配数值的位数变化。

 1 template<typename T>
 2 void print(const vector<T>& v) {
 3     for(vector<int>::const_iterator it=v.begin(); it != v.end(); it++)
 4            cout << *it << " ";
 5         cout << endl;
 6 }
 7 
 8 int decimal_num(int num, int p) {
 9     assert(num >0 && p > 0);
10     int s = 1;
11     while (p-->0) s *= 10;
12     return ((num%s)*10)/s;
13 }
14 
15 template<typename _InIt>
16 void counter_sort(_InIt first, _InIt last, _InIt result, int k, int p) {
17     typedef typename iterator_traits<_InIt>::value_type _ValueType;
18 
19     vector<_ValueType> v(k, _ValueType(0));
20     for(_InIt it=first; it!=last; ++it) v[decimal_num(*it, p)]++;
21 
22     typename vector<_ValueType>::iterator itx = v.begin()+1;
23     for(; itx!=v.end() ; ++itx) (*itx) += *(itx-1);
24 
25     for (int i = last - first -1; i>=0; i--) {
26         _ValueType val = *(first+i);
27         _ValueType idx = decimal_num(val, p);
28         29         *(result + v[idx] - 1) = val;
30         v[idx]--;
31     }
32 
33     for(_InIt it  = first; it!=last; ++it) *it = *result++;
34 }
35 
36 template<typename _InIt>
37 void radix_sort(_InIt first, _InIt last, _InIt result, int p) {  /* 参数p是要排序的整数的最大位数,比如998就是3位数 p=3 */
38     for (int i=1; i<=p; i++) {
39         counter_sort(first, last, result, 10, i);
40     }
41 }
42 
43 int main() {
44     int lst[] = {2,5,0,3,2,3,0,3};
45     vector<int> v(lst, lst+8);
46     vector<int> v2(v.size(), 0);
47 
48     counter_sort(v.begin(), v.end(), 6, v2.begin());
49     print(v2);
50 
51     //int lst2[] = {329,457,657,839,436,720,355};
52     int lst2[10] = {20, 80, 90, 589, 998, 965, 852, 123, 456, 789};
53     vector<int> v3(lst2, lst2+10);
54     vector<int> v4(v3.size(), 0);
55 
56     radix_sort(v3.begin(), v3.end(), v4.begin(), 3);
57     print(v4);
58 
59     return 0;
60 }

遇到的问题:

1、取十进制每个位的值,一开始我是采用pow函数,后来测试发现pow(10.0,2)居然等于99.0,所以自己实现了10的平方运算。

2、每次基数排序时要使用上一次的排序结果,就是计数排序之后多了一个重新赋值的操作,这个开销是否可以去掉,想交替使用2个vector应该可以解决问题,但是需要确定最后的结果在哪个vector里,又对现有函数接口改动太大。

3、数值运算过多,还需要考虑乘法运算溢出。

补充一个运行结果:

1 0 0 2 2 3 3 3 5
2 20 80 90 123 456 589 789 852 965 998
3 
4 Process returned 0 (0x0)   execution time : 0.100 s
5 Press any key to continue.

偷懒图便利就手工测试啦,后面的问题越来越难,还是得弄一下gtest。

为了解决上面赋值问题,重新设计了函数接口,如果位数为奇数就是第二个vector,如果是偶数就是第一个带初始数据的vector为最后的排序结果:

 1 template<typename _InIt>
 2 void counter_sort(_InIt first, _InIt last, _InIt result, int k, int p) {
 3     typedef typename iterator_traits<_InIt>::value_type _ValueType;
 4 
 5     vector<_ValueType> v(k, _ValueType(0));
 6     for(_InIt it=first; it!=last; ++it) v[decimal_num(*it, p)]++;
 7 
 8     typename vector<_ValueType>::iterator itx = v.begin()+1;
 9     for(; itx!=v.end() ; ++itx) (*itx) += *(itx-1);
10 
11     for (int i = last - first -1; i>=0; i--) {
12         _ValueType val = *(first+i);
13         _ValueType idx = decimal_num(val, p);
14         _ValueType vv  = v[idx];
15         *(result + v[idx] - 1) = val;
16         v[idx]--;
17     }
18 }
19 
20 template<typename _InIt>
21 void swap_iter(_InIt& it1, _InIt& it2) {
22     _InIt it = it1;
23     it1 = it2;
24     it2 = it;
25 }
26 
27 template<typename _InIt>
28 void swap_iter(_InIt& f1, _InIt& l1, _InIt& f2, _InIt& l2) {
29     swap_iter(f1, f2);
30     swap_iter(l1, l2);
31 }
32 
33 template<typename _InIt>
34 void radix_sort(_InIt first, _InIt last, _InIt result, _InIt result_end, int p) {
35     for (int i=1; i<=p; i++) {
36         counter_sort(first, last, result, 10, i);
37         swap_iter(first, last, result, result_end);
38     }
39 }

问题解决了,但是觉得不是很优雅,后面再迭代吧。

原文地址:https://www.cnblogs.com/danxi/p/6478379.html