Java基数排序(可负数)
一、基数排序(radix sort)
基本思想:一个待排序的数组,按个十百千等位数,来划分,这个数组中的最大值是多少位数,就需要排序多少次,从个位数开始,如下图。
算法思路
i. 开始排个位数
放入的结果为:
取出的结果为:
Ii .开始排十位数(以个位数排序过的数组为基数组)
取出的结果为:
Iii.百位数排序(以十位数排序过的数组为基数组)
取出的结果为
那么这个排序的最终结果就为 2,7,34,45,67,76,367,508,897。
算法步骤
1、找到最大值和最小值,如果最小值小于0,将所有数都加上2147483647。
2、计算最大值有几位数,决定比较次数。
3、定义10个桶,用来存放临时排序的数组。
4、把每一个数字分别计算余数,把余数相同的数字放到同一个桶里。
5、遍历每一个桶,把桶里的数字按顺序取出来,放回原数组。
6、重复第4步到第5步,直到所有位数都比较完毕。
7、把排序好的数组重新加上减去的值。
时间复杂度
基数排序的时间复杂度为O(d*(n+k)),其中d为位数,n为待排序数组长度,k为桶的数量。
空间复杂度
基数排序的空间复杂度为O(n+k),其中n为待排序数组长度,k为桶的数量。
稳定性
基数排序是一种稳定性排序算法,因为在同一位数上,相同的数字会被放到同一个桶里,而桶内的排序是稳定的。
注意:
在基数排序的实现中,如Integer.MIN_VALUE,则会出现整数溢出的情况。为了解决这个问题,可以将所有数都加上 2147483647,这样最小的数就是 2147483647 - 2147483648 = -1。在排序完成后,再将所有数都减去2147483647即可,这里并没有做特殊处理,如果需要做处理,可以参考可通过最小值测试。
三、代码 :
public static int[] radixSort(int[] arr) {
// 找到最大值和最小值
int max = Integer.MIN_VALUE, min = Integer.MAX_VALUE;
for (int i = 0; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min) {
min = arr[i];
}
}
// 如果最小值小于0,将所有数都加上 2147483647
if (min < 0) {
for (int i = 0; i < arr.length; i++) {
arr[i] -= min;
}
max -=min;
}
// 计算最大值有几位数
int maxLength = (int) Math.log10(max) + 1;
// 桶的数量
int bucketCount = 10;
// 用来存放临时排序的数组
int[][] bucket = new int[bucketCount][arr.length];
// 用来存放排序数组在某个值下面的位置
int[] bucketIndex = new int[bucketCount];
// 根据最大长度数,决定比较次数
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
// 把每一个数字分别计算余数
for (int j = 0; j < arr.length; j++) {
int digit = Math.abs(arr[j] / n % 10);
bucket[digit][bucketIndex[digit]] = arr[j];
bucketIndex[digit]++;
}
// 记录arr的位置
int index = 0;
// 遍历取出第n次排序的值,等于0的不需要取
for (int j = 0; j < bucketCount; j++) {
if (bucketIndex[j] != 0) {
for (int k = 0; k < bucketIndex[j]; k++) {
arr[index] = bucket[j][k];
index++;
}
}
// 把数量置为零,因为还有n轮
bucketIndex[j] = 0;
}
}
// 把排序好的arr重新加上减去的值
if (min < 0) {
for (int i = 0; i < arr.length; i++) {
arr[i] += min;
}
}
return arr;
}