计数排序
int类型数组,其中存的是员工的年龄。比如说16 - 150。对于这样的数据来讲,数据状况是受限的。此时如果将数组从小到大进行排序,该如果实现?
这个实现很简单,实现一个统计数组范围从 0 ~ 150,遍历原数组几岁就在统计数组对应的下标出的值 + 1,遍历完后,再遍历一次统计数组,看统计数组哪些下标对应的值不为0,有几说明对应的年龄就有几个人,写回到老数组即可完成排序。整体时间复杂度O(N)。
public void countSort(int[] arr) {
int max = Integer.MIN_VALUE;
//选出最大值
for (int i = 0; i < arr.length; i++) {
Math.max(max, arr[i]);
}
//数组长度取年龄最大值 + 1
int[] count = new int[max + 1];
for (int j = 0; j < arr.length; j++) {
count[arr[j]]++;
}
int i = 0;
for (int k = 0; k < count.length; k++) {
while (count[k]-- > 0) {
arr[i++] = k;
}
}
}
这种排序称为计数排序,计数排序所用的思想是桶排序的思想,桶就是容器的意思,题中的年龄就是桶。
这样来看,从排序特征上来分大致可分为两类:不基于比较的排序和比较排序。其中桶排序就是不基于比较的一个。
计数排序不是基于比较,根据年龄,放入不同的桶中,最后从桶中获取,一个比较行为都没有。
不基于比较的排序,一定是数据范围比较特殊。所以,不基于比较的排序的扩展性相较于基于比较的排序来讲,可扩展性比较小,因为基于比较的排序实现了一个对数器之后,所有的基于比较的排序都可以用,而不基于比较的不可以。需要根据具体的样本特征来具体的分析。
基数排序
基数排序对数据范围的约束大概在非负的,用十进制表示的数(负的找到最小值,数组整体加成正数也行)。
假设int[] {103,13,27,25,17,9}。
先将数组中最大值103找出来,算出十进制的位数(几次能将10除完),得出是3。
将其余元素不满3位的高位补0,补充之后是{103,013,027,025,017,009}。
准备0 ~ 9一共10个桶,数组从左往右,根据个位数放进对应下标的桶中。
放入后,从0桶开始,依次倒出并清空桶(根据个位数排序)。
根据十位数大小,再次放入桶中。
再次倒出桶中元素,并清空桶。
第三次,将百位数放入桶中后再次倒出。
倒出后,数组有序{009,013,017,025,027,103}
代码实现:
代码中,没有使用队列的方式来创建桶,而是用了另一种方式。
public static void RadixSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
// L。。。R进行排序
public static void radixSort(int[] arr, int L, int R, int digit) {
final int radix = 10;
int i = 0, j = 0;
// 有多少个数准备多少个辅助空间
int[] help = new int[R - L + 1];
for (int l = 1; l < digit; l++) {
int[] count = new int[radix];
for (i = L; i <= R; i++) {
j = getDigit(arr[i], l);
//统计,数组中每个数按照个位十位等拆开后,0~9范围上每一个数共出现了几次
count[j]++;
}
//求出所有数的前缀和
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
//从尾向前遍历
for (i = R; i >= L; i--) {
//再次获取当前数,并算出个位数等。。
j = getDigit(arr[i], l);
//count[j] - 1 确定出当前数应该落在数组位置的下标。
//比如 当前j = 2,则说明前缀和count数组中, <= 2 的时 0 ~ 1 范围,
// 并且我还是arr从后向前遍历arr,所以arr的位置就应该在count[j] - 1上。
help[count[j] - 1] = arr[i];
//对应位置数量 - 1
count[j]--;
}
}
}
public static int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
//找到数组中最大值
for (int i = 0; i < arr.length; i++) {
Math.max(max, arr[i]);
}
int res = 0;
//根据最大值/10,找到十进制位数
while (max != 0) {
res++;
max /= 10;
}
return res;
}
//d = 1,2,3 求出x的个位十位百位
public static int getDigit(int x, int d) {
return ((x / ((int) Math.pow(10, d - 1))) % 10);
}