基数排序(Java)
基数排序(Java)
文章目录
- 基数排序(Java)
- 1.概念
- 2.基数排序、计数排序、桶排序的区别
- 3.基本思想
- 4.代码
- 5.八百万数据测试
- 6.完整代码(负数也可以排序)
1.概念
基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。
基数排序是效率高并且稳定的排序法。(所谓稳定是什么呢?比如对一个原始数组:3,1,43,1 排序,使用基数排序后的数组为 1,1,3,43,第一个1在前面,第二个1在后面)
2.基数排序、计数排序、桶排序的区别
基数排序有两种方法:
这三种排序算法都利用了桶的概念,但对桶的使用方法上有明显差异:
- 基数排序:根据键值的每位数字来分配桶;
- 计数排序:每个桶只存储单一键值;
- 桶排序:每个桶存储一定范围的数值;
3.基本思想
1.将所有待比较数值统一为同样的数位长度,数位较短的数前面补零,然后,从最低位开始,依次进行一次排序,这样一直到最低位排序到最高位排序完成以后,数组就变成了一个有序序列
2.如果比较难理解,请看下面图文解释
原始数组:53,3,542,748,14,214
初始先定义十个桶(十个一维数组)
第一轮排序
1.将每个元素的个位数取出,然后看这个数应该放在哪个对应的桶(一个一维数组)
2.按照桶的顺序(一维数组的下标)依次取出数据,放入原来的数组
数组的第一轮排序:542,53,3,14,214,748
第二轮排序
1.将每个元素的十位数取出,然后看这个数应该放在哪个对应的桶(一个一维数组),如果桶里有数据,会将原数据覆盖
2.按照桶的顺序(一维数组的下标)依次取出数据,放入原来的数组
数组的第二轮排序:3,14,214,542,748,53
第三轮排序
1.将每个元素的百位数取出,然后看这个数应该放在哪个对应的桶(一个一维数组),如果桶里有数据,会将原数据覆盖
2.按照桶的顺序(一维数组的下标)依次取出数据,放入原来的数组
数组的第三轮排序:3,14,53,214,542,748
排序多少轮,取决于最大数的位数,比如最大数是百位就三轮,千位就四轮
4.代码
思路分析代码:
package DataStructures.Sort;import java.util.Arrays;public class RadixSort {public static void main(String[] args) {int[] arr = new int[]{53, 3, 542, 748, 14, 214};radixSort(arr);}/*** @param arr* @author ZJ* Description 基数排序* date 2022-05-11 22:01:50 22:01*/public static void radixSort(int[] arr) {//定义一个二维数组,表示10个桶,每个桶就是一个一维数组int[][] bucket = new int[10][arr.length];//很明显,基数排序使用了空间换时间//为了记录每个桶中实际存放了多少个数据,定义一个一维数组来记录每次放入数据的个数//比如bucketElementCounts[0]=3,意思是bucket[0]存放了3个数据int[] bucketElementCounts = new int[10];int digitOfElement = 0;//每次取出的元素的位数//第一轮排序(针对每个元素的个位进行排序处理)for (int j = 0; j < arr.length; j++) {digitOfElement = arr[j] % 10;//取出每个元素的个位bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];//放入对应的桶bucketElementCounts[digitOfElement]++;}//按照这个桶的顺序(一维数组的下标取出数据),放入原来的数组int index = 0;//遍历每一个桶,并将桶中数据放入原数组for (int k = 0; k < bucketElementCounts.length; k++) {//如果桶中有数据,我们才放到原数组if (bucketElementCounts[k] != 0) {//循环第k个桶,放入for (int l = 0; l < bucketElementCounts[k]; l++) {arr[index] = bucket[k][l];index++;}}bucketElementCounts[k] = 0;//置零!!!!!}System.out.println("第一轮排序后" + Arrays.toString(arr));//第二轮排序(针对每个元素的十位进行排序处理)for (int j = 0; j < arr.length; j++) {digitOfElement = arr[j] / 10 % 10;//取出每个元素的个位bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];//放入对应的桶bucketElementCounts[digitOfElement]++;}//按照这个桶的顺序(一维数组的下标取出数据),放入原来的数组index = 0;//遍历每一个桶,并将桶中数据放入原数组for (int k = 0; k < bucketElementCounts.length; k++) {//如果桶中有数据,我们才放到原数组if (bucketElementCounts[k] != 0) {//循环第k个桶,放入for (int l = 0; l < bucketElementCounts[k]; l++) {arr[index] = bucket[k][l];index++;}}bucketElementCounts[k] = 0;}System.out.println("第二轮排序后" + Arrays.toString(arr));//第三轮排序(针对每个元素的百位进行排序处理)for (int j = 0; j < arr.length; j++) {digitOfElement = arr[j] / 100 % 10;//取出每个元素的个位bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];//放入对应的桶bucketElementCounts[digitOfElement]++;}//按照这个桶的顺序(一维数组的下标取出数据),放入原来的数组index = 0;//遍历每一个桶,并将桶中数据放入原数组for (int k = 0; k < bucketElementCounts.length; k++) {//如果桶中有数据,我们才放到原数组if (bucketElementCounts[k] != 0) {//循环第k个桶,放入for (int l = 0; l < bucketElementCounts[k]; l++) {arr[index] = bucket[k][l];index++;}}bucketElementCounts[k] = 0;}System.out.println("第三轮排序后" + Arrays.toString(arr));}
}
优化后代码:
package DataStructures.Sort;import java.util.Arrays;public class RadixSort {public static void main(String[] args) {int[] arr = new int[]{53, 3, 542, 748, 14, 214};radixSort(arr);}/*** @param arr* @author ZJ* Description 基数排序* date 2022-05-11 22:01:50 22:01*/public static void radixSort(int[] arr) {//定义一个二维数组,表示10个桶,每个桶就是一个一维数组int[][] bucket = new int[10][arr.length];//很明显,基数排序使用了空间换时间//为了记录每个桶中实际存放了多少个数据,定义一个一维数组来记录每次放入数据的个数//比如bucketElementCounts[0]=3,意思是bucket[0]存放了3个数据int[] bucketElementCounts = new int[10];int digitOfElement = 0;//每次取出的元素的位数//找到数组中最大数的位数int max = 0;for (int i = 0; i < arr.length; i++) {if (max < String.valueOf(arr[i]).length()) {max = String.valueOf(arr[i]).length();}}int index = 0;for (int i = 0, n = 1; i < max; i++, n *= 10) {//第i+1轮排序(针对每个元素的位进行排序处理)for (int j = 0; j < arr.length; j++) {digitOfElement = arr[j] / n % 10;//取出每个元素的位bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];//放入对应的桶bucketElementCounts[digitOfElement]++;}//按照这个桶的顺序(一维数组的下标取出数据),放入原来的数组index = 0;//遍历每一个桶,并将桶中数据放入原数组for (int k = 0; k < bucketElementCounts.length; k++) {//如果桶中有数据,我们才放到原数组if (bucketElementCounts[k] != 0) {//循环第k个桶,放入for (int l = 0; l < bucketElementCounts[k]; l++) {arr[index] = bucket[k][l];index++;}}bucketElementCounts[k] = 0;//置零!!!!!}System.out.println("第" + i + 1 + "轮排序后" + Arrays.toString(arr));}}
}
但是上面这个代码不能排序负数
5.八百万数据测试
public static void main(String[] args) {// int[] arr = new int[]{53, 3, 542, 748, 14, 214};
// radixSort(arr);//8000000数组测试int[] arr = new int[8000000];for (int i = 0; i < 8000000; i++) {arr[i] = (int) (Math.random() * 800000000);//生成[0,800000000)的随机数}Date date1 = new Date();SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");String date1Str = simpleDateFormat.format(date1);System.out.println("排序前时间是" + date1Str);radixSort(arr);Date date2 = new Date();String date2Str = simpleDateFormat.format(date2);System.out.println("排序后时间是" + date2Str);}
八千万数据可能会导致内存不足
6.完整代码(负数也可以排序)
分别创建一个正数数组和一个负数数组,正数数组按照上面的代码进行基数排序,负数数组的元素先乘上-1,都变成正数,再按照基数排序,最后从后往前乘上-1添加到arr数组里
package DataStructures.Sort;import java.util.Arrays;public class RadixSort {public static void main(String[] args) {int[] arr = new int[]{0, -44, 56, 0, 0, 89, -456, -12, 46, 9, 13, 45, -545, 0};radixSort(arr);System.out.println(Arrays.toString(arr));}/*** @param arr* @author ZJ* Description 基数排序* date 2022-05-11 22:01:50 22:01*/public static void radixSort(int[] arr) {int length = 0;//定义一个二维数组,表示10个桶,每个桶就是一个一维数组int[][] bucketGreaterZero = new int[10][arr.length];//很明显,基数排序使用了空间换时间int[][] bucketLessZero = new int[10][arr.length];//负数部分的数组//为了记录每个桶中实际存放了多少个数据,定义一个一维数组来记录每次放入数据的个数//比如bucketElementCounts[0]=3,意思是bucket[0]存放了3个数据int[] bucketGreaterZeroElementCounts = new int[10];int[] bucketLessZeroElementCounts = new int[10];int digitOfGreaterElement = 0;//每次取出的元素的位数int digitOfLessElement = 0;// int[] arrGreaterZero = new int[arr.length];
// int[] arrLessZero = new int[arr.length];int i = 0;int index1 = 0;int index2 = 0;for (i = 0; i < arr.length; i++) {//先计算出arr数组里负数和正数分别有几个,再创建正数数组和负数数组(不然合并数组时容易数组越界)if (arr[i] >= 0) {index1++;//正数个数(包含0)} else {index2++;//负数个数}}int[] arrLessZero = new int[index2];int[] arrGreaterZero = new int[arr.length - index2];index1 = 0;index2 = 0;//分别往正数数组和负数数组里添加数据for (i = 0; i < arr.length; i++) {if (arr[i] >= 0) {arrGreaterZero[index1] = arr[i];index1++;} else {arrLessZero[index2] = arr[i];index2++;}}//让负数数组里的数据变成正数for (i = 0; i < arrLessZero.length; i++) {if (arrLessZero[i] != 0) {arrLessZero[i] *= (-1);}}//找到正数数组中最大数的位数int maxGreaterZero = 0;for (i = 0; i < arrGreaterZero.length; i++) {if (maxGreaterZero < String.valueOf(arrGreaterZero[i]).length()) {maxGreaterZero = String.valueOf(arrGreaterZero[i]).length();}}//找到负数数组中最大数的位数int maxLessZero = 0;for (i = 0; i < arrLessZero.length; i++) {if (maxLessZero < String.valueOf(arrLessZero[i]).length()) {maxLessZero = String.valueOf(arrLessZero[i]).length();}}int index = 0;int n1 = 1;for (i = 0, n1 = 1; i < maxLessZero; i++, n1 *= 10) {//第i+1轮排序(针对每个元素的位进行排序处理)for (int j = 0; j < arrLessZero.length; j++) {digitOfLessElement = arrLessZero[j] / n1 % 10;//取出每个元素的位bucketLessZero[digitOfLessElement][bucketLessZeroElementCounts[digitOfLessElement]] = arrLessZero[j];//放入对应的桶bucketLessZeroElementCounts[digitOfLessElement]++;}//按照这个桶的顺序(一维数组的下标取出数据),放入原来的数组index = 0;//遍历每一个桶,并将桶中数据放入原数组for (int k = 0; k < bucketLessZeroElementCounts.length; k++) {//如果桶中有数据,我们才放到原数组if (bucketLessZeroElementCounts[k] != 0) {//循环第k个桶,放入for (int l = 0; l < bucketLessZeroElementCounts[k]; l++) {arrLessZero[index] = bucketLessZero[k][l];index++;}}bucketLessZeroElementCounts[k] = 0;//置零!!!!!}
// System.out.println("第" + i + 1 + "轮排序后" + Arrays.toString(arr));}
// for (int x = index2 - 1; x >= 0; x--) {// if (arrLessZero[x] != 0) {// temp[length] = (arrLessZero[x] * (-1));
// length++;
// }
// }//临时数组,从后往前遍历负数数组元素再乘上-1,保存到temp数组里,这样负数部分就排序好了int[] temp = new int[arrLessZero.length];int index3 = 0;for (i = arrLessZero.length - 1; i >= 0; i--) {temp[index3] = arrLessZero[i] * (-1);index3++;}int n2 = 1;for (i = 0, n2 = 1; i < maxGreaterZero; i++, n2 *= 10) {//第i+1轮排序(针对每个元素的位进行排序处理)for (int j = 0; j < arrGreaterZero.length; j++) {digitOfGreaterElement = arrGreaterZero[j] / n2 % 10;//取出每个元素的位bucketGreaterZero[digitOfGreaterElement][bucketGreaterZeroElementCounts[digitOfGreaterElement]] = arrGreaterZero[j];//放入对应的桶bucketGreaterZeroElementCounts[digitOfGreaterElement]++;}//按照这个桶的顺序(一维数组的下标取出数据),放入原来的数组index = 0;//遍历每一个桶,并将桶中数据放入原数组for (int k = 0; k < bucketGreaterZeroElementCounts.length; k++) {//如果桶中有数据,我们才放到原数组if (bucketGreaterZeroElementCounts[k] != 0) {//循环第k个桶,放入for (int l = 0; l < bucketGreaterZeroElementCounts[k]; l++) {arrGreaterZero[index] = bucketGreaterZero[k][l];index++;}}bucketGreaterZeroElementCounts[k] = 0;//置零!!!!!}
// System.out.println("第" + i + 1 + "轮排序后" + Arrays.toString(arr));}
// for (int x = 0; x < index1; x++) {// temp[length] = arrGreaterZero[x];
// length++;
// }//合并数组System.arraycopy(temp, 0, arr, 0, temp.length);//把temp数组里的元素复制到arr数组里System.arraycopy(arrGreaterZero, 0, arr, temp.length, arrGreaterZero.length);//把arrGreaterZero数组里的元素合并到arr数组里}
}
说明:
1)基数排序说对传统桶排序的扩展,速度很快
2)基数排序是经典的空间换时间方式,占用内存很大,当对海量内存排序时,容易造成OutOfMemoryError
3)基数排序是稳定的
基数排序(Java)相关推荐
- 桶排序和基数排序 Java
桶式排序 概念 有限个数字m,每个数字的大小都在1与n之间,则我们可以假设有n个桶,遍历m个数字,将其存入对应的桶中(如数字的值为3,就存入3号桶,桶的值对应存入数字的个数) 例子 有数字3,3,5, ...
- 基数排序 java实现
package sort; //基数排序:稳定排序 public class RadixSort { // d为数据长度,根据数据长度排几次序 private static int[] radixSo ...
- java 桶排序_[图解] 桶排序
桶排序是一种排序的思想,其实现包括计数排序和基数排序两种,冒泡排序.选择排序.插入排序.归并排序.快速排序和堆排序都是基于比较的排序,而桶排序提出了一种新的思路,即基于数据状态的排序. 1. 桶排序的 ...
- 各种排序算法及其java程序实现
原文:http://blog.csdn.net/t12x3456/article/details/7430700 各种排序算法:冒择路(入)兮(稀)快归堆,桶式排序,基数排序 冒泡排序,选择排序,插入 ...
- java各种排序实现
排序是程序开发中一种非常常见的操作,对一组任意的数据元素(或记录)经过排序操作后,就可以把他们变成一组按关键字排序的有序队列. 对一个排序算法来说,一般从下面3个方面来衡量算法的优劣: 时间复杂度:它 ...
- 机器学习面试问题汇总
1监督与非监督区别 2L1L2区别 3生成模型和判别模型区别 像贝叶斯lda 等就是生成模型计算过概率分布之类的 1svm算法的原理如何组织训练数据如何调节惩罚因子如何防止过拟合svm的泛化能力增量学 ...
- 机器学习应届面试会问到的面试问题汇总
转载:https://blog.csdn.net/q383700092/article/details/58605715 1监督与非监督区别 2L1L2区别 3生成模型和判别模型区别 像贝叶斯lda ...
- 机器学习面试问题汇总—史上最详细
本人计算机小白一枚,将自己学到的知识点整理出来,一方面是对自己学习的小总结,另一方面是欢迎大家批评指正.如果觉得写得还可以,大家可以转发关注此博客,谢谢!后续会有新算法持续更新~.. . ------ ...
- 【原创】远景能源面试--一面
首先就是你在学校的研究方向是什么?---->云计算和大数据 这个方向有点太大了,那说说你为啥要选择这个方向,现在做了哪些研究? 1.HashMap和HashTable HashMap是Hasht ...
最新文章
- 什么是以太坊?它到底怎么运作的?
- linux延时与定时计算
- Py之pyecharts:python包之数据可视化包pyecharts简介、安装、使用方法之详细攻略
- 0-1背包问题(C语言)
- BoooLee pyretoolkit -- 一个基于python re模块的在线正则表达式测试工具
- poll函数实现多路复用
- java printf与println_浅析Java中print、printf、println的区别
- MATLAB 画图 字符串连接表示 plot参数
- ltp︱基于ltp的无监督信息抽取模块(事件抽取/评论观点抽取)
- Java——设计模式(装饰模式_IO)
- vue computed 传参_前端面试时面试官想要听到什么答案(关于一些Vue的问题)
- 由线性空间V中的集合生成的子空间
- 小象机器学习(邹博老师)学习笔记
- 软件系统分析设计过程方法精要
- 针对不同场景的Python合并多个Excel方法
- Aptana工具介绍
- RC电路充放电时间的计算
- 你绝对能懂的“腐烂的橘子”解法
- 数据解读 | 广东省内,谁才有资格做下一个深圳?
- c#利用log4记录日志