28.基数排序

计数排序

非常简单,但是数值范围比较大了就不行了

算法思想:

假如有150个数字在 0-100之间需要排序
按 0,1,2,3,.....100 数字放101个桶,每次遇见相应的数字吧它放到桶里去。
最后从0,1,2.....100依次出桶就有序了。

基数排序

算法思想:

非负数排序:先按个位数排序,再按十位数排序....
如果有负数:找出最小负数,所有数都-最小负数,然后就转换成非负数排序。最后再所有数都加最小负数

代码如下:

	public static void sort() {
		// 如果会溢出,那么要改用long类型数组来排序
		// 找到数组中的最小值
		int min = arr[0];
		for (int i = 1; i < n; i++) {
			min = Math.min(min, arr[i]);
		}
		int max = 0;
		for (int i = 0; i < n; i++) {
			// 数组中的每个数字,减去数组中的最小值,就把arr转成了非负数组
			arr[i] -= min;
			// 记录数组中的最大值
			max = Math.max(max, arr[i]);
		}
		// 根据最大值在BASE进制下的位数,决定基数排序做多少轮
		radixSort(bits(max));
		// 数组中所有数都减去了最小值,所以最后不要忘了还原
		for (int i = 0; i < n; i++) {
			arr[i] += min;
		}
	}

	// 返回number在BASE进制下有几位
	public static int bits(int number) {
		int ans = 0;
		while (number > 0) {
			ans++;
			number /= BASE;
		}
		return ans;
	}

	// 基数排序核心代码
	// arr内要保证没有负数
	// m是arr中最大值在BASE进制下有几位
	public static void radixSort(int bits) {
		// 理解的时候可以假设BASE = 10
		for (int offset = 1; bits > 0; offset *= BASE, bits--) {
			Arrays.fill(cnts, 0);
			for (int i = 0; i < n; i++) {
				// 数字提取某一位的技巧
				cnts[(arr[i] / offset) % BASE]++;
			}
			for (int i = 1; i < BASE; i++) {
				cnts[i] = cnts[i] + cnts[i - 1];
			}
			for (int i = n - 1; i >= 0; i--) {
				// 前缀数量分区的技巧
				// 数字提取某一位的技巧
				help[--cnts[(arr[i] / offset) % BASE]] = arr[i];
			}
			for (int i = 0; i < n; i++) {
				arr[i] = help[i];
			}
		}
	}

基数排序的实现细节,非常优雅的一个实现
关键点:前缀数量分区的技巧、数字提取某一位的技巧
时间复杂度O(n),额外空间复杂度O(m),需要辅助空间做类似桶的作用,来不停的装入、弹出数字

一般来讲,计数排序要求,样本是整数,且范围比较窄
一般来讲,基数排序要求,样本是10进制的非负整数
如果不是就需要转化,代码里做了转化,并且代码里可以设置任何进制来进行排序
一旦比较的对象不再是常规数字,那么改写代价的增加是显而易见的,所以不基于比较的排序并不通用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值