C++ 基数排序的实现实例代码

所属分类: 软件编程 / C 语言 阅读数: 71
收藏 0 赞 0 分享

C++ 基数排序

 大家好,今天带来的是自己实现的用C++完成基数排序.在数据结构,算法分析和程序设计的学习过程中,我们经常也无法避免的要学到排序的算法.排序算法是程序设计过程中使用频率极高的算法之一,其输入是一组无序的序列,要求以升序或者降序的方式输出一组有序的序列.对于如二分查找等算法,要求输入是有序的序列,也就是要先排序后查找,由此可见排序算法的重要性.

  广为人知的排序算法有冒泡排序,还有选择排序,插入排序.高级一些的有快速排序,希尔排序,堆排序,归并排序,基数排序等. 其中时间复杂度为O(n*logn)的算法有快速排序,归并排序和堆排序等,其中快速排序的使用最为广泛.时间复杂度为O(n2)的算法有冒泡排序,选择排序和插入排序等.

  基数排序是一种非比较的排序算法,它是以桶排序为基础的.它们在现代计算机出现之前,一直被用于老式穿孔卡的排序.

  基数排序的基本思想是:一共有10个"桶",代表各个数位为0~9.在每个桶上,组织一个优先队列,对于输入的一系列正整数和0,按照个位的大小关系分别进入10个"桶"中.然后遍历每个"桶",按照十位的大小关系进行调整,紧接着是百位,千位.......直到到达最大数的最大位数.结合图例,我们可以理解这个算法:

  在C++实现中,我用到了队列这一数据结构作为每个"桶"的组织方式,因为取数总是从最下方取,而放入数这是放入"桶"的顶部,这与队列的队头出对,队尾入队的方式相似.对10个"桶"的组织,则采用向量vector.这个程序支持,输入序列一定范围内不限个数,且在int数据类型表示范围内的非负数排序,根据最大数的位数来决定排序趟数.将数据类型从int改为long或者long long ,则可对更大的整数排序.

  排序函数如下:

 1 vector<int> radix_sort(vector<int> in)
 2 {
 3   vector<queue<int>> bucket(10);      //十个桶为一个向量,每个桶又是一个队列
 4   int max_value = in.at(0);
 5 
 6   for (auto &i : in)
 7   {
 8     if ( i > max_value)
 9       max_value = i;
 10   }                               //找出输入的最大元素
 11 
 12   int n = 0;
 13 
 14   for (; max_value != 0; max_value /= 10, ++n)
 15     ;                             //得到最多位数,也即排序趟数
 16 
 17   for (auto &i : in)
 18     bucket.at(0).push(i);               //全部放入第一个桶
 19 
 20   int i = 0;                         //趟数控制变量
 21   int m = 0;                        //提取各个位数有关的控制变量
 22   int k = 0;                         //桶数控制变量
 23   int x = 0;                         //桶的大小,因动态改变了容器,迭代器会失效,不使用迭代器
 24   int y = 0;                         //桶内部控制变量
 25   int j = 0;                         
 26   int item = 0;                        //桶内元素
 27 
 28   for (; i < n ; ++i)                    //趟数循环
 29   {
 30     for ( k = 0; k < 10 ; ++k)    //遍历每个桶
 31     {
 32       x = bucket.at(k).size();
 33 
 34       if ( !x )
 35         continue;
 36 
 37       for (y = 0; y < x ; ++y)                 //遍历桶中队列的元素
 38       {
 39         item = j = bucket.at(k).front();
 40 
 41         for (m = i; m > 0; --m)               //提取出各个位
 42           j /= 10;
 43 
 44         switch (j % 10)                   //进入相应的桶
 45         {
 46           case 0:
 47             bucket.at(0).push(item);
 48             break;
 49 
 50           case 1:
 51             bucket.at(1).push(item);
 52             break;
 53 
 54           case 2:
 55             bucket.at(2).push(item);
 56             break;
 57 
 58           case 3:
 59             bucket.at(3).push(item);
 60             break;
 61 
 62           case 4:
 63             bucket.at(4).push(item);
 64             break;
 65 
 66           case 5:
 67             bucket.at(5).push(item);
 68             break;
 69 
 70           case 6:
 71             bucket.at(6).push(item);
 72             break;
 73 
 74           case 7:
 75             bucket.at(7).push(item);
 76             break;
 77 
 78           case 8:
 79             bucket.at(8).push(item);
 80             break;
 81 
 82           case 9:
 83             bucket.at(9).push(item);
 84             break;
 85 
 86           default:                     //异常检测,捕捉与处理
 87             try
 88             {
 89               throw runtime_error("Error!");
 90             }
 91             catch (runtime_error err)
 92             {
 93               cout << err.what() << endl;
 94               exit(EXIT_FAILURE);
 95             }
 96         }
 97 
 98         bucket.at(k).pop();
 99       }
100     }
101   }
102 
103   vector<int> out;                        //定义一个新的向量,将所有桶的数据收集起来作为最后结果
104 
105   for ( i = 0; i < 10; ++i )
106   {
107     int num = bucket.at(i).size();
108 
109     for (int ai = 0; ai < num; ++ai)
110     {
111       out.push_back( bucket.at(i).front() );
112       bucket.at(i).pop();
113     }
114   }                                            //排序结果到一个向量中
115 
116   return out;                         //返回这个有序的序列
117 
118 }

    算法要得到正确结果,要注意的是同一个桶的元素的顺序,是从下至上递增的,这是由遍历时从代表0的"桶"开始和从桶中取     元素时是从下取保证的.再有,最后从桶中取出元素时也要注意顺序.

更多精彩内容其他人还在看

用标准c++实现string与各种类型之间的转换

这个类在头文件中定义, < sstream>库定义了三种类:istringstream、ostringstream和stringstream,分别用来进行流的输入、输出和输入输出操作。另外,每个类都有一个对应的宽字符集版本
收藏 0 赞 0 分享

C++如何通过ostringstream实现任意类型转string

再使用整型转string的时候感觉有点棘手,因为itoa不是标准C里面的,而且即便是有itoa,其他类型转string不是很方便。后来去网上找了一下,发现有一个好方法
收藏 0 赞 0 分享

C/C++指针小结

要搞清一个指针需要搞清指针的四方面的内容:指针的类型,指针所指向的类型,指针的值或者叫指针所指向的内存区,还有指针本身所占据的内存区
收藏 0 赞 0 分享

C++ 类的静态成员深入解析

在C++中类的静态成员变量和静态成员函数是个容易出错的地方,本文先通过几个例子来总结静态成员变量和成员函数使用规则,再给出一个实例来加深印象
收藏 0 赞 0 分享

C++类的静态成员初始化详细讲解

通常静态数据成员在类声明中声明,在包含类方法的文件中初始化.初始化时使用作用域操作符来指出静态成员所属的类.但如果静态成员是整型或是枚举型const,则可以在类声明中初始化
收藏 0 赞 0 分享

C++类静态成员与类静态成员函数详解

静态成员不可在类体内进行赋值,因为它是被所有该类的对象所共享的。你在一个对象里给它赋值,其他对象里的该成员也会发生变化。为了避免混乱,所以不可在类体内进行赋值
收藏 0 赞 0 分享

C++中的friend友元函数详细解析

友元可以是一个函数,该函数被称为友元函数;友元也可以是一个类,该类被称为友元类。友元函数的特点是能够访问类中的私有成员的非成员函数。友元函数从语法上看,它与普通函数一样,即在定义上和调用上与普通函数一样
收藏 0 赞 0 分享

static全局变量与普通的全局变量的区别详细解析

以下是对static全局变量与普通的全局变量的区别进行了详细的分析介绍,需要的朋友可以过来参考下,希望对大家有所帮助
收藏 0 赞 0 分享

C++ explicit关键字的应用方法详细讲解

C++ explicit关键字用来修饰类的构造函数,表明该构造函数是显式的,既然有"显式"那么必然就有"隐式",那么什么是显示而什么又是隐式的呢?下面就让我们一起来看看这方面的知识吧
收藏 0 赞 0 分享

教你5分钟轻松搞定内存字节对齐

随便google一下,人家就可以跟你解释的,一大堆的道理,我们没怎么多时间,讨论为何要对齐.直入主题,怎么判断内存对齐规则,sizeof的结果怎么来的,请牢记以下3条原则
收藏 0 赞 0 分享
查看更多