算法导论第三版

时间:2021-11-18 09:56:20

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

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

 

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 }

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