zoukankan      html  css  js  c++  java
  • 算法导论第三版--计数,基数排序

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

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

    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 }

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

  • 相关阅读:
    System.BadImageFormatException: Could not load file or assembly
    MSSQL数据库索引的应用
    快递api网接口快递调用方法
    winform的扩展的带有截图功能picturebox
    免费api大全
    C#使用百度API通过IP获取地理位置和坐标
    用淘宝ip地址库查ip
    开源相关社区/项目一览(备查,欢迎补充)(转)
    .NET系列文章——近一年文章分类整理,方便各位博友们查询学习(转)
    设计模式--状态模式C++实现
  • 原文地址:https://www.cnblogs.com/danxi/p/6478379.html
Copyright © 2011-2022 走看看