十大排序之『桶排序』(计数、桶、基数排序)

八、计数排序

原理:遍历数列,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;
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值