基数排序
2015-07-16 08:59
621 查看
基数排序简介
基数排序不同于快排、堆排等常规比较排序,属于非比较排序(我更喜欢叫它分发-收集排序)。它效率很高,平均时间复杂度为O(rn),系数r的大小取决于基数(radix)的选择。这个时间复杂度是近似线性的!不过也有一定的局限性,只适用于正整数或者一定限制的字符串,而且内存花销比较大。
正整数基数排序
基数排序,排序时有不同的基准字,每一个基准字都有自己的优先性。举一个例子
对于数组
a = {92,435,21,54,1345,423,9908},排序依据的基准字是每个数字的每一个数位,每一个基准字都有优先性则是说优先按照哪一个基准字进行排序(这里低位优先,原因之后解释)。
我们假设有10个桶(bucket)依次编号
0-9用来代表10个不同的基准字(数位)。
按照低位优先,我们分离出每个数字个位,个位是x就扔到编号为x的bucket中(这个过程形象的称之为分发)
bucket | number |
---|---|
0 | |
1 | 21 |
2 | 92 |
3 | 423 |
4 | 54 |
5 | 435,1345 |
6 | |
7 | |
8 | 9908 |
9 |
这样
a = 21,92,423,54,435,1345,9908
之后,以十位数位基准,再次分发
bucket | number |
---|---|
0 | 9908 |
1 | |
2 | 21,423 |
3 | 435 |
4 | 1345 |
5 | 54 |
6 | |
7 | |
8 | |
9 | 92 |
a = 9908,21,423,435,1345,54,92
重复以上过程,直到a中最大的位数(千位,9908)也被分配-收集完毕
这样依次输出a中的值,就是有序的。
Q:为什么低位优先?
A:优先级是我们人为认定的。对于正整数来说,最高位的数字对数字的大小影响是最大的,如果从高位到低位排序,那么可能会出现较大的数字的低位比较小的数字的低位小,从而使得较大的数字被排到较小数的后面这种荒唐的情况。因此我们从低位开始分配-收集,这个过程中保证了基数排序是稳定的。
基数排序的思想十分简单,不过可能实现起来比较困难。从图表中可以看出,每一个
bucket中存储的数据的个数是不一定的,一维数组肯定不行,然而二维数组就会大量的浪费空间(类似于稀疏图的存储)。因此我们必须用链表来储存数字,这会使得代码比较麻烦。不过使用c++的
vector可以轻松解决这个问题。
下面给出c++的实现代码:
int GetBucketPos(int number,int pos)//得到数据number第pos位数的"桶号" { int temp = 1; for(int i = 0 ; i < pos - 1 ; ++i) temp *= 10; return (number / temp) % 10; } void ClearBucket(vector<int> (&bucket) [10]) { for(int i = 0 ;i < 10 ; ++i) bucket[i].clear(); } void RadixSort(vector<int>& vec) { vector<int> bucket[10];//"桶" int maxPos = 10;//最大数字的位数,我这里随便写一个 int cnt; for(int pos = 1 ; pos <= maxPos ; ++pos){//从数字的个位开始 for(int i = 0 ; i < vec.size() ; ++i){ int index = GetBucketPos(vec[i],pos);//得到vec[i]的pos位数的桶号 bucket[index].push_back(vec[i]);//分发到桶里 } //收集... cnt = 0;//新的vec数组的下标 for(int i = 0 ; i < 10 ; ++i)//i是桶的个数 for(int j = 0 ; j < bucket[i].size() ; ++j)//每个桶中的数字个数 vec[cnt++] = bucket[i][j]; //收集完毕后桶清空 ClearBucket(bucket); } }
字符串基数排序
基数排序一般来讲更多的用于正整数,但是实际上经过一定的约束也可以用于字符串的排序。基数排序的要求有基准字和优先级,要完成字符串的基数排序,必须解决这两个问题。
1.字符串的”桶号”如何高效确定?
一般来讲字符串都是数字+大小写字母的组合。但是数字和字母的ASCII码并不是连续的,所以用一串“连续的桶号”就需要做点手脚。可以用
map来映射,然而一般情况下可以直接设定一个简单的映射数组来表示
static const string stringTable = " 0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
那么每一个字符的桶号就是在
stringTable的下标。
(注意
stringTable中的字符必须严格按照ASCII递增的顺序)
2.字符串的排序优先级如何确定
注意字符串和数字有很大不同,数字肯定是越长越大,而字符串的大小和长度毫无关系,在GetBucketPos(string,pos)函数中,如果
pos >= string.size()的时候该如何去选择呢?
举个例子:
abc和
abcd,显然
abcd更大一些,比较时还是从低位比较,
abcd低位是
d,而
abc的长度不够,理论上来讲应该为空。又
abc比
abcd要小,所以我们人为的添加一个空字符,它是最小的,这里我建议用
space来代替。
下面给出字符串基数排序的c++代码:
int GetBucketPos(const string& s,int pos)//得到string第pos个字符的"桶号” { char ch; if(pos >= s.size())// ch = ' ';//位数不够补空字符 else ch = s[pos]; for(int i = 0 ; i < stringTable.size() ; ++i) if(stringTable[i] == ch) return i; } void ClearBucket(vector<string> (&bucket) [63])//63个桶 { for(int i = 0 ; i < 63; ++i) bucket[i].clear(); } void RadixSort(vector<string>& vec) { vector<string> bucket[63];//63是stringTable的长度 int maxPos = 25;//随便写的一个,可根据实际情况更改,vec[]中最长的字符串的长度 int cnt; for(int pos = maxPos - 1 ; pos >= 0 ; --pos){//字符串的最低"有效位"开始 //对于每一字符 分配 for(int i = 0 ; i < vec.size() ; ++i){ int index = GetBucketPos(vec[i],pos);//得到vec[i]的第pos个字符的"桶号“ bucket[index].push_back(vec[i]);//分配 } //收集... cnt = 0;//收集前置零 for(int i = 0 ; i < 63; ++i)//i是桶的个数 for(int j = 0 ; j < bucket[i].size() ; ++j)//每个桶的字符串个数 vec[cnt++] = bucket[i][j]; //收集完毕后桶清空 ClearBucket(bucket); } }
相关文章推荐
- hdu 1171 Big Event in HDU
- GetWindowText
- hdu 1171 Big Event in HDU
- Javascript函数的参数
- PGM格式文件
- unlock 306sh(Aquos Crystal 解锁)
- 用GDB调试程序(三)
- 【java工具类】DES加密
- scikit-learn:5. 加载内置公用的数据
- eclipse 设置编辑区、控制台、xml等文本文件字体大小
- 用GDB调试程序(六)
- ccbpm工作流引擎是如何支持多种流程模式的
- c++ for_each transform的区别
- iOS中实现视图的拖动
- eclipse 工程配置(.classpath和.project)文件的作用
- 用GDB调试程序(七)
- 用GDB调试程序(五)
- [idea]对于前端里面一些事情的看法
- MySQL连不上,报Host is blocked because of many connection errors; unblock with 'mysqladmin flush-hosts'
- 用GDB调试程序(二)