1、基数排序
- Radix Sort:一种用空间换时间的排序算法,速度很快,但只能用于整数排序;
- 步骤:
- ① 定义10个桶,代表0~9这十个数;
- ② 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零;
- ③ 只看个位,对所有数执行放桶操作,完成第一轮排序;
- ④ 只看十位,对所有数执行放桶操作,完成第二轮排序;
- ⑤ ... ...
- ⑥ 多轮操作之后,数组就是有序的。
- 稳定性:稳定;
- 复杂度:O(n);
- 弊端:内存占用较大,对海量数据排序容易造成内存溢出。
2、Java代码
package Algorithm.Sort;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
/**
* @author yhx
* @date 2020/10/09
*/
public class RadixSort {
private static final int NUM = 80000;
public static void main(String[] args) {
// 定义待排序数组
int[] arr1 = {53, 3, 542, 748, 14, 214};
int[] arr2 = {53, 3, 542, 748, 14, 214};
int[] arr3 = new int[NUM];
for (int i = 0; i < NUM; i++) {
arr3[i] = (int) ((Math.random() * 2 - 1) * NUM);
}
Radix radix = new Radix();
// 基数排序基本步骤演示
System.out.println("基数排序基本步骤演示: ");
radix.radixSortProcess(arr1);
System.out.println();
// 整合后的基数排序
System.out.println("整合后的基数排序: ");
radix.radixSort(arr2);
System.out.println("排序后,arr2[] = " + Arrays.toString(arr2));
System.out.println();
// 用大量数据测试基数排序的排序速度
System.out.println("基数排序效率测试:");
// 时间标记精确到毫秒
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
// Date()函数用于标记时间
Date date1 = new Date();
String date1Str = simpleDateFormat.format(date1);
System.out.println("arr3[]排序前的时间是:" + date1Str);
radix.radixSort(arr3);
Date date2 = new Date();
String date2Str = simpleDateFormat.format(date2);
System.out.println("arr3[]排序后的时间是:" + date2Str);
// System.out.println("arr3[] = " + Arrays.toString(arr3));
}
}
class Radix {
public final int NUM10 = 10;
/**
* 基数排序的步骤演示
* @param arr 待排序数组
*/
public void radixSortProcess(int[] arr) {
// 第一轮:针对每个数的个位进行排序
/*
1、定义一个二维数组,共10行,表示10个桶
2、为了防止放入数时发生溢出,定义每个桶的大小为arr.length,所以桶排序(基数排序)是空间换时间的一种排序方式
*/
int[][] bucket = new int[10][arr.length];
// 为了记录每个桶中存放了多少个数据,我们定义一个一维数组记录每个桶中放入的数据个数
int[] bucketElementCounts = new int[10];
for (int i : arr) {
// 取出每个元素的个位
int digitOfElement = i % 10;
// 放入桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = i;
bucketElementCounts[digitOfElement]++;
}
// 将桶中的数放回arr
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置为0
bucketElementCounts[k] = 0;
}
System.out.println("第1轮,arr[] = " + Arrays.toString(arr));
// 第二轮:针对每个数的十位进行排序
for (int i : arr) {
// 取出每个元素的个位
int digitOfElement = i / 10 % 10;
// 放入桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = i;
bucketElementCounts[digitOfElement]++;
}
// 将桶中的数放回arr
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;
}
System.out.println("第2轮,arr[] = " + Arrays.toString(arr));
// 第三轮:针对每个数的百位进行排序
for (int i : arr) {
// 取出每个元素的个位
int digitOfElement = i / 100 % 10;
// 放入桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = i;
bucketElementCounts[digitOfElement]++;
}
// 将桶中的数放回arr
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;
}
System.out.println("第3轮,arr[] = " + Arrays.toString(arr));
}
/**
* 整合后的基数排序(桶排序)
* 一般的基数排序只能排序正数
* 对于负数,此处使用一种转换的办法:
* 1、先找到数组中最小的负数
* 2、数组所有元素减去该负数
* 3、基数排序
* 4、每个元素再加上之前减去的数
*
* @param arr 待排序数组
*/
public void radixSort(int[] arr) {
// 步骤1:得到数组中最小的数
int min = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] < min) {
min = arr[i];
}
}
// 步骤2:数组所有元素减去该负数
if (min < 0) {
for (int i = 0; i < arr.length; i++) {
arr[i] -= min;
}
}
// 步骤3-1:得到数组中最大的数的位数
int max = arr[0];
for (int i = 1; i < arr.length; i++) {
if (arr[i] > max) {
max = arr[i];
}
}
// 用一种巧妙的办法求一个数的位数
int maxLength = (max + "").length();
// 步骤3-2:基数排序
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts = new int[10];
for (int i = 0, n = 1; i < maxLength; i++, n *= NUM10) {
for (int j : arr) {
// 取出每个元素的个位
int digitOfElement = j / n % 10;
// 放入桶中
bucket[digitOfElement][bucketElementCounts[digitOfElement]] = j;
bucketElementCounts[digitOfElement]++;
}
// 将桶中的数放回arr
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置为0
bucketElementCounts[k] = 0;
}
}
// 步骤4:每个元素再加上之前减去的数
if(min<0) {
for (int i = 0; i < arr.length; i++) {
arr[i] += min;
}
}
}
}
3、运行结果
基数排序基本步骤演示:
第1轮,arr[] = [542, 53, 3, 14, 214, 748]
第2轮,arr[] = [3, 14, 214, 542, 748, 53]
第3轮,arr[] = [3, 14, 53, 214, 542, 748]
整合后的基数排序:
排序后,arr2[] = [3, 14, 53, 214, 542, 748]
基数排序效率测试:
arr3[]排序前的时间是:2020-10-09 15:50:53:978
arr3[]排序后的时间是:2020-10-09 15:50:53:994