桶排序(bucketSort)代码分析:
对于桶排序代码思路较为简单:
1. 求出数列中的最大值
2. 设计桶的数目等于最大值加一(因为是非负数数据,所以不存在无穷的情况,分析出最大值的情况下,数据最大能够达到的也就是max,最小为0)
3. 将整数数据放入到桶中,此时数据一旦放入到桶中一次,则桶的计数加一(记录着这个桶中一共有多少个数据)
4. 利用for循环将数据重新排列好赋值给arr,完成排序。
// only for 0~200 value
public static void bucketSort(int[] arr) {
//排序中这个if都有,不多分析
if (arr == null || arr.length < 2) {
return;
}
//如果需要判断最大值,则先设目前最大值为系统最小值
int max = Integer.MIN_VALUE;
//一次遍历求出数组最大值
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
//求出数组最大之后开始新建桶
int[] bucket = new int[max + 1];
//将数组中的数据放入到桶中,并且将桶中的计数加一
for (int i = 0; i < arr.length; i++) {
bucket[arr[i]]++;
}
//下面这个for循环及i可以写成不一样的形式,但是总体没有区别,就不写了
int i = 0;
//将整个桶再遍历一遍
for (int j = 0; j < bucket.length; j++) {
//如果此时桶中的数据大于0,则意味着桶中还保存着数据,执行循环操作
//如果此时桶中数据取完,则bucket[j]数据为0,退出循环
while (bucket[j]-- > 0) {
arr[i++] = j;
}
}
}
基数排序(radixSort)代码分析:
基数排序同样利用了桶排序,但是思路是完全不一样的,桶排序利用数据从高位开始每一位的大小来排序,如果在高位上数据不同,则可以直接从小到大排列,如果高位相同,则同样可以利用低位数据进行判断是否一致。
总体思路:
1. 首先求出最大数值是几位数(求出几位数之后方便之后进行几次的模10操作)
2. 将数组中数据每一位上进行比较,所以在每一位上设置十个桶,进行判断当前位上数据大小
2.1 进行判断的过程中,由于最高位有最高的话语权,所以我们从低到高进行运算,如果最高位比较大,则此时拥有绝对的话语权。
2.2 判断过程中,每一次循环当前位,都需要进行一次数据的排序,,所以需要求出当前数据应该排在整个数组中的位置。
// only for no-negative value
public static void radixSort(int[] arr) {
if (arr == null || arr.length < 2) {
return;
}
radixSort(arr, 0, arr.length - 1, maxbits(arr));
}
//算出来最大数是几位数
public static int maxbits(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
max = Math.max(max, arr[i]);
}
int res = 0;
while (max != 0) {
res++;
max /= 10;
}
return res;
}
//进行整个排序操作
public static void radixSort(int[] arr, int begin, int end, int digit) {
//数据如果从个位数来看,都属于0-9,所以总共是十个数,也就分为10个桶
final int radix = 10;
//此处仅仅是简化后面代码的书写,也可以不要
int i = 0, j = 0;
//可以将count理解为桶,用来保存数据在0-9上出现的次数
int[] count = new int[radix];
//bucket为最后保存数据的桶,他其实只有保存的操作,可以将count和bucket换一下命名会更加容易理解
int[] bucket = new int[end - begin + 1];
//digit为最大值所占的位数,下面的循环整体应该是在每一位上进行数据的比较,
//从而得到整体的大小排序
//从低位开始进行排序,如果当前位置小,则排在前面
for (int d = 1; d <= digit; d++) {
//首先将count都设为0,为了方便后面次数的加减
for (i = 0; i < radix; i++) {
count[i] = 0;
}
//将当前数据的倒数d位上的数据取出进行判断,进行桶排序
for (i = begin; i <= end; i++) {
j = getDigit(arr[i], d);
count[j]++;
}
// 对于上面的count,将count数组中的个数求和,当前存储的个数为之前所有数目的总和。
for (i = 1; i < radix; i++) {
count[i] = count[i] + count[i - 1];
}
// 此时将数据从尾到头重新遍历一遍,j依旧为倒数第d位上的数据
//这个函数的目的是将保存在count中的数目从小到大全部存放到桶bucket中
for (i = end; i >= begin; i--) {
j = getDigit(arr[i], d);
//此时count中为所有数目的总和,所以此时count中的数据表示当前位数
//从而可以利用这一点将这一位上数据相等的排好序保存到桶中
bucket[count[j] - 1] = arr[i];
//此时第count[j]位置上已经放了数,则此时开始让count[j]-1,这个位置就是当前位数相同,但之前位数较小的数据应该存放的位置
count[j]--;
}
for (i = begin, j = 0; i <= end; i++, j++) {
arr[i] = bucket[j];
}
}
}
//获取倒数第d项上的数值
public static int getDigit(int x, int d) {
return ((x / ((int) Math.pow(10, d - 1))) % 10);
}
由于数据从最低位开始,所以从第一次最低位开始排序,进行下一次循环的时候,如果当前位置相等,则之前在数组中靠后的数据较大,排在前面位置的数据较小,在循环中可以发现是从后往前依次保存数据到bucket中的,则在满足当前数值排序的前提下,之前的顺序信息也保留了下来,实现了从最低位到最高位依次遍历从而实现整个数组的排序。