计数,基数的中文读音都一样,这翻译的人还嫌我们计算机不够乱,真的想吐槽。
不管了,毕竟代码还是不一样的。
1、计数排序(counter sort):
通过一个上限来统计集合里的数值(或者其他非数值类型映射的数值),并累计比小于自己(包括)的数值的统计的个数,从而形成排序的索引(也就是前面有多少个小于我的,我的位置就确定了)。
普通计数排序代码:(仍需优化,valuetype默认是整数类型)
1 template2 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 template2 void print(const vector & 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 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 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 template2 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 21 void swap_iter(_InIt& it1, _InIt& it2) { 22 _InIt it = it1; 23 it1 = it2; 24 it2 = it; 25 } 26 27 template 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 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 }
问题解决了,但是觉得不是很优雅,后面再迭代吧。