内排序算法(二)
前面介绍的几种排序方式主要是以元素比较作为为基本的运算单元,这种排序方式所能达到的上限是
桶排序
对于桶排序有很多种理解和实现,我们先看一种最简单的,这种方式是借助一组向量做类似key-value
形式的存储,其中key
为待排数组的值,value
为其出现的次数。排序时将待排数组按照key-value
存放到桶中,然后再对桶按照index
从小到大输出value
即可。
待排数组: 7 3 8 9 6 1 8 1 2
空桶: 0 0 0 0 0 0 0 0 0
建桶(count): 0 2 1 1 0 0 1 1 2 1
桶index: 0 1 2 3 4 5 6 7 8 9
可以看到,我们只需要将桶按照index输出其对应的value个key即可完成排序
void bucketSort(vector<int>& v){
vector<int> bucket(v.size(),0);
for(auto x:v){
bucket[x] ++;
}
int index = 0;
for(int i=0;i<bucket.size();i++){
for(int j=0; j<bucket[i];j++){
v[index++] = i;
}
}
}
上述代码的时间复杂度为
另一种桶排序的方式为使用有限个桶,将待排数据分散到几个有序的桶中,每个桶中的数据再进行单独排序。桶内排序完成后,再把每个桶中的数据按照顺序依次取出,组成的序列就是有序的了。
待排数组:
26,7,30,5,8,22,11,38,29,35,10
桶状态:
|5,7,8| | 10,11 | | 22,26,29| | 30,35,38 |
|-----| |-------| |---------| | ---------|
| 0-9 | | 10-19 | | 20-29 | | 30-39 |
我们来分析一下这种桶排序方式的时间复杂度,如果待排序数据有n
个,我们将它们划分到m
个桶内,则每个桶内有k=n/m
个元素
计数排序(Counting Sort)
所谓计数排序是指在桶排序的基础上,增加一个数组保存每个元素的累计值,还是桶排序的第一个例子
待排数组: 7 3 8 9 6 1 8 1 2
index: 0 1 2 3 4 5 6 7 8 9
count: 0 2 1 1 0 0 1 1 2 1
前若干个桶 0 1 2 3 4 5 6 7 8 9
累计的count: 0 2 3 4 4 4 5 6 8 9
改进后的桶内存的元素表示原数组元素对应排序后的index,例如,待排数组中的最后一个元素为2,通过查total
数组,可知total[2] = 3
,即2
的前面有2个元素,2是新数组的第3个元素,因此将2放入到数组2-1=1
的位置,同时令count-1
。
void bucketSort(vector<int>& v){
//copy original array
vector<int> tmpArray(v);
//建桶
vector<int> bucket(v.size(),0);
for(auto x:v){
bucket[x] ++;
}
//更新桶内count值
for(int i=1; i<v.size();i++){
bucket[i] = bucket[i]+bucket[i-1];
}
//产生排序数组
for(int i=0;i<v.size();i++){
int x = tmpArray[i];
//x在bucket[x]-1的位置
int index = bucket[x]-1;
//将x放到排序后的位置
v[index] = x;
}
}
- 算法分析
- 数组长度为
n
, 所有记录区间[0, m)
上 - 时间代价
- 总的时间代价为
- 适用于
相对于 很小的情况,即待排数组比较紧凑的情况
- 总的时间代价为
- 空间代价:
个计数器,长度为 的临时数组,