基数排序(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)相关推荐

  1. 桶排序和基数排序 Java

    桶式排序 概念 有限个数字m,每个数字的大小都在1与n之间,则我们可以假设有n个桶,遍历m个数字,将其存入对应的桶中(如数字的值为3,就存入3号桶,桶的值对应存入数字的个数) 例子 有数字3,3,5, ...

  2. 基数排序 java实现

    package sort; //基数排序:稳定排序 public class RadixSort { // d为数据长度,根据数据长度排几次序 private static int[] radixSo ...

  3. java 桶排序_[图解] 桶排序

    桶排序是一种排序的思想,其实现包括计数排序和基数排序两种,冒泡排序.选择排序.插入排序.归并排序.快速排序和堆排序都是基于比较的排序,而桶排序提出了一种新的思路,即基于数据状态的排序. 1. 桶排序的 ...

  4. 各种排序算法及其java程序实现

    原文:http://blog.csdn.net/t12x3456/article/details/7430700 各种排序算法:冒择路(入)兮(稀)快归堆,桶式排序,基数排序 冒泡排序,选择排序,插入 ...

  5. java各种排序实现

    排序是程序开发中一种非常常见的操作,对一组任意的数据元素(或记录)经过排序操作后,就可以把他们变成一组按关键字排序的有序队列. 对一个排序算法来说,一般从下面3个方面来衡量算法的优劣: 时间复杂度:它 ...

  6. 机器学习面试问题汇总

    1监督与非监督区别 2L1L2区别 3生成模型和判别模型区别 像贝叶斯lda 等就是生成模型计算过概率分布之类的 1svm算法的原理如何组织训练数据如何调节惩罚因子如何防止过拟合svm的泛化能力增量学 ...

  7. 机器学习应届面试会问到的面试问题汇总

    转载:https://blog.csdn.net/q383700092/article/details/58605715 1监督与非监督区别 2L1L2区别 3生成模型和判别模型区别 像贝叶斯lda ...

  8. 机器学习面试问题汇总—史上最详细

    本人计算机小白一枚,将自己学到的知识点整理出来,一方面是对自己学习的小总结,另一方面是欢迎大家批评指正.如果觉得写得还可以,大家可以转发关注此博客,谢谢!后续会有新算法持续更新~.. . ------ ...

  9. 【原创】远景能源面试--一面

    首先就是你在学校的研究方向是什么?---->云计算和大数据 这个方向有点太大了,那说说你为啥要选择这个方向,现在做了哪些研究? 1.HashMap和HashTable HashMap是Hasht ...

最新文章

  1. 什么是以太坊?它到底怎么运作的?
  2. linux延时与定时计算
  3. Py之pyecharts:python包之数据可视化包pyecharts简介、安装、使用方法之详细攻略
  4. 0-1背包问题(C语言)
  5. BoooLee pyretoolkit -- 一个基于python re模块的在线正则表达式测试工具
  6. poll函数实现多路复用
  7. java printf与println_浅析Java中print、printf、println的区别
  8. MATLAB 画图 字符串连接表示 plot参数
  9. ltp︱基于ltp的无监督信息抽取模块(事件抽取/评论观点抽取)
  10. Java——设计模式(装饰模式_IO)
  11. vue computed 传参_前端面试时面试官想要听到什么答案(关于一些Vue的问题)
  12. 由线性空间V中的集合生成的子空间
  13. 小象机器学习(邹博老师)学习笔记
  14. 软件系统分析设计过程方法精要
  15. 针对不同场景的Python合并多个Excel方法
  16. Aptana工具介绍
  17. RC电路充放电时间的计算
  18. 你绝对能懂的“腐烂的橘子”解法
  19. 数据解读 | 广东省内,谁才有资格做下一个深圳?
  20. c#利用log4记录日志

热门文章

  1. 【每日一题 6.25】粉刷房子
  2. 如何利用Visual Studio建立具有MVC框架的网页模型
  3. linux mysql使用教程_Linux 下MySQL的安装及基本使用
  4. WiFi基础学习心得
  5. 如何备份和恢复iOS应用程序的数据和设置
  6. iOS毛玻璃效果(不需要任何第三方了)
  7. 总体标准差和标准差的区别
  8. mysql考勤数据库设计,mysql – 跟踪学生出勤的数据库设计
  9. Apache Tomcat 漏洞利用
  10. Android 获取4g信号强度