首页 > 编程语言 >常见排序算法之基数排序

常见排序算法之基数排序

时间:2023-01-18 23:33:52浏览次数:53  
标签:10 arr int 算法 基数排序 bucketElementCounts 数组 排序


文章目录

  • ​​1、概述​​
  • ​​2、测试代码​​
  • ​​3、测试小案例​​



1、概述

基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是透过键值的部份资讯,将要排序的元素分配至某些“桶”中,藉以达到排序的作用,基数排序法是属于稳定性的排序,其时间复杂度为O (nlog®m),其中r为所采取的基数,而m为堆数,在某些时候,基数排序法的效率高于其它的稳定性排序法。

核心思想:根据我们数字在不同位置上的大小,不断地循环(先看个位,然后十位,然后百位…),将对应位置上的数据进行放置,不同的数字放在不同的桶中,相同的数字放在相同的桶中。最终我们能够得到对应的有序数组


以数组 [ 2,7,11,12,312,213,14,27,37,44 ]为例:

常见排序算法之基数排序_数据



第一次排序后的数组,当作第二次排序的数组 [ 11,12,312,2,213,14,44,37,27,7 ]:

常见排序算法之基数排序_git_02



第二次排序后的数组,当作第三次排序的数组 [ 2,7,11,12,312,213,14,27,37,44 ]:

常见排序算法之基数排序_数据_03

最终的到有序的数组:2,7,11,12,14,27,37,44,213,312



补充:

  1. 本次待排序的数组中最大的数字为3位数,所以只需要循环三次就可以得到对应的结果
  2. 我们第三次循环的时候,百位上为0的数字(一位数,两位数)它们全部都放在0号桶的位置,数字会越来越集中
  3. 数字的极端情况为都是相同的数字,所以我们桶的大小为数组的长度
  4. 整个排序需要开辟的空间:(10(桶数量) * arr.length * 4(字节) / 1024 / 1024 / 1024 )G
  5. 当数据量比较大的时候,上千万的数据(具体的大小根据自己电脑的内存而定)时会出现OOM



2、测试代码

核心步骤梳理:

1、找到我们的最大值,确定该数字的位数,为了后期的循环次数做准备

2、定义相应的参数:设置我们的桶及桶中的位置(二维数组),对应桶中的数字个数

3、遍历我们数组中的数字,根据对应位上(个位、十位…)的值,放到对应的桶中

4、不断的循环,直至循环完成

5、将我们桶中的数据转移到我们对应的数组中(不断地遍历我们地桶及桶中的位置)

6、遍历完成以后,及时清除桶中的数据

最大数字的位数大小,决定了整个排序算法的外层循环次数

public class RadixSortTest01 {
public static void main(String[] args) {
int[] arr = {2, 312, 44, 14, 12, 7, 213, 27, 11, 37};
radixSort(arr);
System.out.println("基数排序后的数组为:" + Arrays.toString(arr));
}

public static void radixSort(int[] arr) {
//1.找到待排序的数组中的最大的数字
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i + 1];
}
}
//2.获取到最大数字有多少位
int maxLength = (max + "").length();

//3.定义一个二维数组:相当于一个含有是个10桶位置,且每个桶的大为数组的长度(防止出现极端情况)
int[][] bucket = new int[10][arr.length];


//4.记录每一个桶里面,实际存放的数据的个数(由于有10个桶,所以我们设置数组的大小为10)
//bucketElementCounts[0]=3 : 第一个桶位置的数据个数为3个
int[] bucketElementCounts = new int[10];


//5.由个位开始依次往前遍历,循环的次数是由最大数字的位数决定的
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
//6.遍历数组中的每一个数字,根据其对应位置上的数字,放到指定桶的指定位置
for (int j = 0; j < arr.length; j++) {
//7.获取到我们待排序数组指定的位置上的指定位数
int digitOfElement = arr[j] / n % 10;

//8.将我们遍历到的数字,放到桶中的相应的位置
//digitOfElement 表示具体的位置,用于指定对应的桶
//bucketElementCounts[digitOfElement] 表示根据对应的桶位置,获取到对应桶位置下面的元素信息
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
//9.前面我们添加了数字,所以我们对应桶下的数字个数需要+1
bucketElementCounts[digitOfElement]++;
}

//10.将我们之前放在桶中的数据,返回放到我们对应的数组中
int index = 0;
//11.遍历10个桶
for (int k = 0; k < bucketElementCounts.length; k++) {
if (bucketElementCounts[k] != 0) {
//12.如果桶中的数据不为空,那么我们就将对应位置上的数字,赋值到我们的原来数组中(直接覆盖之前的值)
for (int l = 0; l < bucketElementCounts[k]; l++) {
//13.进行赋值,并且index不断地+1
arr[index] = bucket[k][l];
index++;
}
}
//14.每次成功转移数据后,需要清空我们桶中对应位置的数据
bucketElementCounts[k] = 0;
}

}
}
}



不得不说,在大量数据,并且对应数据中最大值比较小的情况下(五位数的数字和八位数的数字相比,也就只多了三次循环),排序所花费的时间是十分少的。不过该排序的一个最大的弊端就是十分占用系统资源,对应的二维数组中,我们每一个桶的大小都是整个数组的长度。 该排序算法,以空间换时间。 本排序算法无法比较负数,若需要比较负数则需要再进行优化。



3、测试小案例

利用我们以有的条件,进行一个小案例的测试:计算将数组长度为80000的乱序数组排序所花费的时间

package pers.mobian.jishu;

import java.util.Arrays;

public class RadixSortTest02 {
public static void main(String[] args) {
//创建一个大小为8000000的数组,并且完成赋值
int[] arr = new int[8000000];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int) (Math.random() * 800000);
}


long start = System.currentTimeMillis();
//使用对应的基数排序
radixSort(arr);
long end = System.currentTimeMillis();


System.out.println("基数排序时间为:" + (end - start) + "ms");


}


public static void radixSort(int[] arr) {
//找到待排序的数组中的最大的数字
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i + 1];
}
}
//获取到最大数字有多少位
int maxLength = (max + "").length();

//定义一个二维数组:相当于一个含有是个10桶位置,且每个桶的大为数组的长度(防止出现极端情况)
int[][] bucket = new int[10][arr.length];


//记录每一个桶里面,实际存放的数据的个数(由于有10个桶,所以我们设置数组的大小为10)
//bucketElementCounts[0]=3 : 第一个桶位置的数据个数为3个
int[] bucketElementCounts = new int[10];


//循环的次数是由最大数字的位数决定的
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
for (int j = 0; j < arr.length; j++) {
//获取到我们待排序数组指定的位置上的指定位数
int digitOfElement = arr[j] / n % 10;

//将我们遍历到的数字,放到桶中的相应的位置
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
bucketElementCounts[digitOfElement]++;
}

//将我们之前放在桶中的数据,返回放到我们对应的数组中
int index = 0;
for (int k = 0; k < bucketElementCounts.length; k++) {
if (bucketElementCounts[k] != 0) {
for (int l = 0; l < bucketElementCounts[k]; l++) {
arr[index] = bucket[k][l];
index++;
}
}
//每次成功转移数据后,需要清空我们桶中对应位置的数据
bucketElementCounts[k] = 0;
}

}
}
}


标签:10,arr,int,算法,基数排序,bucketElementCounts,数组,排序
From: https://blog.51cto.com/u_15942107/6019569

相关文章

  • 常见排序算法之归并排序
    文章目录​​1、概述​​​​2、测试代码​​​​3、小案例​​1、概述归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(DivideandConquer)......
  • 常见排序算法之希尔排序
    文章目录​​1、概述​​​​2、希尔排序之交换法​​​​3、希尔排序之移动法​​​​4、测试案例​​1、概述由于简单的插入排序每次数据量变多的时候,数据需要移动且交换......
  • m基于遗传优化算法的公式参数拟合matlab仿真
    1.算法描述遗传算法的原理        遗传算法GA把问题的解表示成“染色体”,在算法中也即是以二进制编码的串。并且,在执行遗传算法之前,给出一群“染色体”,也即是假......
  • 常见排序算法之选择排序
    文章目录​​1、概述​​​​2、代码实现​​​​3、测试代码​​1、概述选择排序(Selectionsort)是一种简单直观的排序算法。它的工作原理是:第一次从待排序的数据元素中选出......
  • 常见排序算法之冒泡排序
    文章目录​​1、概述​​​​2、传统代码​​​​3、优化后代码​​​​4、测试案例​​1、概述冒泡排序(BubbleSort),是一种的较简单且常见的的排序算法。它重复地访问排序的......
  • 常见排序算法之插入排序
    文章目录​​1、概述​​​​2、代码实现​​​​3、测试代码​​1、概述插入排序,一般也被称为直接插入排序。对于少量元素的排序,它是一个有效的算法。插入排序是一种最简......
  • 17种编程语言实现排序算法-冒泡排序
    开源地址https://gitee.com/lblbc/simple-works/tree/master/sort/bubbleSort1.安卓Java版privatevoidsort(int[]array){for(inti=0;i<array.length......
  • 司守奎《数学建模算法与应用》课后习题:线性规划
    写在最前面:    我是一个刚学数模的小白,觉得把自己的思路和代码啊公式写出来能提升学习效率,在参考了司守奎老师的《数学建模算法与应用》(第二版)一书后想把自己的想......
  • java-数组相关的算法(尚硅谷)
    1.数组元素的赋值(杨辉三角、回形数等)2.求数值型数组中元素的最大值、最小值、平均数、总和等3.数组的复制、反转、查找(线性查找、二分法查找)4.数组元素的排序算法一......
  • J Tokitsukaze and Sum of MxAb【2023牛客寒假算法基础集训营2】
    J TokitsukazeandSumofMxAb原题链接题意给出长为n的序列,对于所有的i,j求max\((|a_i-a_j|,|a_i+a_j|)\)之和思路对于两个负数\(a_i\)和\(a_j\),max\((|a_i-......