八、计数排序
原理:遍历数列,count[a[i]]++,最后再把count数组中的元素倾倒出来
缺陷:如果仅仅按照上诉算法,不能排序负数,并且count数组的大小与数列中max的大小相同,空间浪费大
优化:扫描数列记录min与max,count数组的size即为max-min+1,并且范围是从0到size,即记录count[a[i]-min]++,等于将有数据的区块左边界平移至与原点重合,这样既优化空间,又解决了无法存储负数的问题
void CountSort(vector<int>& a) {//计数排序
int n = a.size();
int mx = a[0];
int mn = a[0];
for (int i = 1;i < n;i++) {
mx = max(mx, a[i]);
mn = min(mn, a[i]);
}
int _size = mx - mn + 1;
vector<int> count(_size, 0);//设置记录数组,并全部初始化为0,一定要记得初始化!!!
for (int i = 0;i < n;i++) {//统计每个元素出现的次数
count[a[i]-mn]++;
}
int k = 0;
for (int i = 0;i < _size;i++) {
for (int j = 0;j < count[i];j++) {
a[k++] = i+mn;//记得最后加上mn
}
}
}
九、桶排序
原理:在数据范围(min~max)中分若干个区间(桶),将每个区间(桶)内的元素排序
例如有一组从0到100的数据,就分十个桶,1号桶装0~9,二号桶装10 ~19,以此类推,将每个桶中数据快排,即得到有序数组
由此可见,桶排序更加适合于数据分布均匀、且数据范围较小的情况,否则元素全部集中在某一个桶上,就失去了桶排序的优势,并且要考虑好时间与空间的权衡
桶的大小:BucSize = (mx - mn) / n + 1;//至少为1
桶的个数:BucCnt = (mx - mn) / BucSize + 1;//至少为1
void BucketSort(vector<int>& a) {//桶排序
int n = a.size();
int mx = a[0];//数组中最大值
int mn = a[0];//数组中最小值
for (int i = 1;i < n;i++) {
mx = max(mx, a[i]);
mn = min(mn, a[i]);
}
int BucSize = (mx - mn) / n + 1;//设置桶的大小,至少为1
int BucCnt = (mx - mn) / BucSize + 1;//设置桶的个数,至少为1
vector<vector<int>> buckets(BucCnt);//设置桶(二维数组),存放在某一个范围内的数据(用不同id的桶装不同范围的数据)
for (int i = 0;i < n;i++) {
int idx = (a[i] - mn) / BucSize;//根据数据的大小选择合适的桶
buckets[idx].push_back(a[i]);//将a[i]放进编号为idx的桶里
}
int k=0;
for (int i = 0;i < BucCnt;i++) {
sort(buckets[i].begin(), buckets[i].end());//对每个桶中的数据进行排序
//begin()与end()返回的是首元素与末元素的指针
for (int j = 0;j < buckets[i].size();j++) {
a[k++] = buckets[i][j];//将桶中排好序的数据放回原数组中
}
}
}
十、基数排序
原理:实际上是特殊的桶排序,以最大为百位数的数据举例,先按照个位数排序,再按照十位数排序,最后按照百位数排序,即可得到有序数组。
由于位数只有十个,从0~9,所以可以划分十个桶,将数据按照位数放入相应的桶中,就实现了该位数的排序
注意:数据进桶与出桶符合“先进先出”,这样才能确保有序
优化:区别于桶排序要用二维数组存储桶的编号与该编号下的所有数据,基数排序有一种巧妙的方法,只需要一个一维数组,即十个桶,并且每个编号的桶只存储一个元素——数列中位数等于该编号的数据个数。
然后,运用前缀和的思想,将上个桶中的数据个数倒入下一个桶中(最后一个桶中数据变成了数列中所有元素的个数)。再逆序扫描一边数列,根据数据该位数的大小找到对应的桶,桶中数据-1即为这个数排序后的位置。
void RadixSort(vector<int>& a) {//LSD基数排序
int n = a.size();
int mx = a[0];
for (int i = 0;i < n;i++) {
mx = max(mx, a[i]);//找出最大的元素的位数,以确定排序的趟数
}
int idx = 0;
while (mx > 0) {
mx /= 10;
idx++;
}
int m = 1;
while(idx--) {
vector<int> temp(n);
vector<int> count(10, 0);
for (int i = 0;i < n;i++) {
int dig = (a[i] / m) % 10;//获取a[i]的某位的值
count[dig]++;
}
for (int j = 1;j < 10;j++) {//计算累计次数,用于确定之后a[i]的值在数组中位置
count[j] += count[j - 1];
}
for (int k = n - 1;k >= 0;k--) {// 构建排序后的数组
int dig = (a[k] / m) % 10;
temp[count[dig] - 1] = a[k];//count[dig] - 1即为排序后a[k]应处的位置
count[dig]--;//输出一个后就要减一,先进先出
}
for (int h = 0;h < n;h++) {// 将排序后的数组复制回原数组
a[h] = temp[h];
}
m *= 10;
}
}