基数排序 典型的空间换取时间算法 当数量很大时很容易造成内存溢出
核心思想就是把数组中的每个数先按个位数进行分配 每个数分配到对应数的桶里面
再取出桶中的元素放回原来的数组中
然后依次类推 对更高位的数也是同样的做法
最后获得的数组就是一个有序的数组
注意:这里写的方法中并不能对负数进行判断
package datastructure.sort;
/*
@CreateTime 2021/9/16 11:52
@CreateBy cfk
*/
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53,3,542,748,14,214,-22};
radixSortAll(arr);
System.out.println(Arrays.toString(arr));
// 测试80000条数据 基数排序所需要的时间 1s
//当测试数据为80000000时 对应使用的内存为 80000000 * 11 * 4 /1024 /1024 /1024 3.3G 内存溢出
// Java heap space OOM
// int[] arr = new int[80000];
// for (int i = 0; i < 80000; i++) {
// arr[i] = (int) (Math.random()*8000000);
// }
//
// SimpleDateFormat sdf = new SimpleDateFormat("yy-MM-dd HH:mm:ss");
// String s1 = sdf.format(new Date());
// System.out.println(s1);
//
//
// radixSort(arr);
//
//
// String s2 = sdf.format(new Date());
// System.out.println(s2);
}
//只能对非负数进行排序
public static void radixSort(int[] arr) {
//设置十个桶
int[][] bucket = new int[10][arr.length];
//设置一个数组来记录每个桶中的数量
int[] bucketCount = new int[10];
//获取最大数的位数
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
//这里把数转为字符串 然后获取它的长度即可知道位数
int digit = String.valueOf(max).length();
//根据数组中最大数的位数进行循环遍历对应的次数
for (int k = 0, l=1; k < digit; k++, l *= 10) {
//把数据放入桶中
for (int i = 0; i < arr.length; i++) {
//获取每个数个位元素 1235 个位数字 n % 10 十位数字 n / 10 % 10 百位数字 n / 10 / 10 % 10
int digitOfElement = arr[i] / l % 10;
//把数据放到对应的桶中 并且再同一个桶中放入数据后会自动把指针指向下一位
bucket[digitOfElement][bucketCount[digitOfElement]++] = arr[i];
}
//定义数组的开始索引
int index = 0;
//把数据从桶中取出来
//这里有个问题? 为什么作为桶的数组每次遍历后里面存的数据不需要清空就进行下一下判断呢
//debug后发现原来这里只需要清空记录个数的数组就好了 清楚之后自然就相当于清空了桶
//因为当你把记录数组清空后,你在重新放桶元素时,桶中如果原来位置有元素则被取代
//又由于你对桶中是否有颜色是根据桶中元素的个数判断的,当记录桶元素数组中对应为0,就算桶中有元素也不会读取
for (int i = 0; i < bucket.length; i++) {
if (bucketCount[i] != 0) {
for (int j = 0; j < bucketCount[i]; j++) {
arr[index++] = bucket[i][j];
}
}
//记得把记录的数组置为0否则后面会溢出
bucketCount[i] = 0;
}
}
}
}
处理负数
主要思想是: 如果数组中最小值为负数的话 就让数组全部元素都减去最小值 这样就能保证数组的最小值为0
//对所有的数进行排序
public static void radixSortAll(int[] arr) {
//设置十个桶
int[][] bucket = new int[10][arr.length];
//设置一个数组来记录每个桶中的数量
int[] bucketCount = new int[10];
//获取最大数的位数
int max = arr[0];
//获取最小值
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
if (arr[i] < min){
min = arr[i];
}
}
if (min < 0) {
//让所有数减去最小值
for (int i = 0; i < arr.length; i++) {
arr[i] -= min;
}
//这里记得最大值也要减去因为可以发现位的改变
max -= min;
}
//这里把数转为字符串 然后获取它的长度即可知道位数
int digit = String.valueOf(max).length();
//根据数组中最大数的位数进行循环遍历对应的次数
for (int k = 0, l=1; k < digit; k++, l *= 10) {
//把数据放入桶中
for (int i = 0; i < arr.length; i++) {
//获取每个数个位元素 1235 个位数字 n % 10 十位数字 n / 10 % 10 百位数字 n / 10 / 10 % 10
int digitOfElement = arr[i] / l % 10;
//把数据放到对应的桶中 并且再同一个桶中放入数据后会自动把指针指向下一位
bucket[digitOfElement][bucketCount[digitOfElement]++] = arr[i];
}
//定义数组的开始索引
int index = 0;
//把数据从桶中取出来
//这里有个问题? 为什么作为桶的数组每次遍历后里面存的数据不需要清空就进行下一下判断呢
//debug后发现原来这里只需要清空记录个数的数组就好了 清楚之后自然就相当于清空了桶
//因为当你把记录数组清空后,你在重新放桶元素时,桶中如果原来位置有元素则被取代
//又由于你对桶中是否有颜色是根据桶中元素的个数判断的,当记录桶元素数组中对应为0,就算桶中有元素也不会读取
for (int i = 0; i < bucket.length; i++) {
if (bucketCount[i] != 0) {
for (int j = 0; j < bucketCount[i]; j++) {
arr[index++] = bucket[i][j];
}
}
//记得把记录的数组置为0否则后面会溢出
bucketCount[i] = 0;
}
}
//最后让所有的值加回最小值
if (min < 0) {
for (int i = 0; i < arr.length; i++) {
arr[i] += min;
}
}
}