一、计数排序
稳定排序
适用于小数据范围排序
假设待排序序列的元素范围为0~k-1;创建一个长度为k的数组c,c[x]表示在待排序序列中值小于x的元素个数;则x在排序后的序列中的位置即为c[x]
c++代码:
void CountSort(vector<int>& sequence, int max_k)
{
vector<int> count(max_k+1,0); // 计数数组,max_k为sequence元素的最大值
const int c_len = sequence.size();
vector<int> sort_seq(c_len);
for(int i=0; i<c_len; ++i)
++count[sequence[i]];
for(int i=1; i<=max_k; ++i)
count[i] += count[i-1];
for(int i=c_len-1; i>=0; --i) //c_len->0维持稳定排序
{
sort_seq[count[sequence[i]]-1] = sequence[i]; //-1是因为下标从0开始
--count[sequence[i]]; //使得后续与sequence[i]相同的元素的位置往前挪1
}
sequence = sort_seq;
}
空间复杂度:计数数组count,长度为k;
存储排序完成后的序列的数组sort_seq,长度为n(n为待排序序列长度)
S(n) = θ(n+k)
时间复杂度:第一个for循环次数 x常数 + 第二个for循环次数 x 常数 + 第3个for循环次数 x 常数
T(n) = n x常数 + k x 常数 + n x 常数 = θ(n + k)
如果序列元素的范围太大,比如k=2^32,非常浪费空间;
当k比较小时,T(n)是线性的;
所以计数排序适用于小范围集合排序,比如高考分数排序(总分750,k=750; n很大,几十万学生)
二、桶排序
稳定排序
思想与计数排序相似
计数排序中c[x]就是一个bucket, 桶排序中x表示的是[x1~x2]范围内的数据,即x1<元素<=x2
步骤:
1、创建k个bucket,遍历待排序序列,把元素扔到相应的bucket中
2、对非空的bucket进行排序(可以使用其他排序方法,也可以递归的使用桶排序)
3、按照顺序遍历以上buckets,获得排好序的序列
c++代码:
void BucketSort(vector<int>& sequence, int max_k)
{
const int c_len = sequence.size();
//假设一个bucket的范围为[x1~x2],具体bucket的数量,每个bucket中x1,x2的值根据实际情况定
//这里设置每个bucket中x2-x1=10,第一个bucket的x1为sequence的最小值,设为0
int x1, x2;
int d = 10; //d = x2-x1
int k = ceil((max_k+1)/float(d)); // bucket的数量
vector<vector<int> > buckets(k); //创建k个bucket
//遍历待排序序列将其中的元素分配到相应的bucket中
int i, j;
for(i=0; i<c_len; ++i)
for(j=0; j<k; ++j)
if((sequence[i]>=d*j) && (sequence[i]<d*(j+1)))
buckets[j].push_back(sequence[i]);
//对每个非空的bucket排序
for(j=0; j<k; ++j)
if(!buckets[j].empty())
ShellSort(buckets[j]); //使用希尔排序,也可以使用其他排序方法
//按顺序遍历buckets,获得排好序的序列
i=0;
vector<int>::iterator buck_it;
for(j=0; j<k; ++j)
if(!buckets[j].empty())
{
buck_it = buckets[j].begin();
while(buck_it!=buckets[j].end())
sequence[i++] = *buck_it++;
}
}
空间复杂度:与每个bucket所使用的排序算法有关;
首先所有buckets的长度为n (n为待排序序列长度)
S(n) = n+
公认S(n) = θ(n+k)
时间复杂度:与桶的数量、分配情况等有关
最好情况: T(n) = θ(n)
最坏情况: T(n) = θ(n^2)
平均情况: T(n) = θ(n+k)