1. 基数排序(桶排序)介绍
-
基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort)或bin sort,顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用
-
基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
-
基数排序(Radix Sort)是桶排序的扩展
-
基数排序是1887年赫尔曼·何乐礼发明的。它是这样实现的:将整数按位数切割成不同的数字,然后按每个位数分别比较。
2. 基数排序基本思想
- 将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。
3. 图文说明
- 将数组 {53, 3, 542, 748, 14, 214} 使用基数排序, 进行升序排序。
4. 代码实现
import java.util.Arrays;
public class RadixSort {
public static void main(String[] args) {
int[] arr = {53, 3, 542, 748, 14, 214};
analysisProcess(arr);
int[] arr2 = {53, 3, 542, 748, 14, 214};
System.out.println("排序前" + Arrays.toString(arr2));
radixSort(arr2);
System.out.println("排序后" + Arrays.toString(arr2));
}
/**
* 推导排序过程
* 假设待排序数组是{53, 3, 542, 748, 14, 214}
*
* @param arr
*/
public static void analysisProcess(int[] arr) {
// 定义一个二维数组,表示10个桶,每个桶又是一个一维的数组
// 1.二维数组包含10个一维数组
// 2.为了防止放入数的时候,数据溢出,则每个桶的大小设为arr.length
int[][] bucket = new int[10][arr.length];
// 第1轮(针对元素的个位进行排序)
// 为记录每个桶中,存了多少数据,定义一个一维数组来记录
int[] bucketElementCounts = new int[10];
for (int j = 0; j < arr.length; j++) {
// 取出元素的个位的值
int num = arr[j] / 1 % 10;
// 放入到对应的桶中
bucket[num][bucketElementCounts[num]] = arr[j];
// 后移一位用来放下次进来的数
bucketElementCounts[num]++;
}
int index = 0;
// 遍历每一个桶,从数组中依次取出数据,放入原来的数组
for (int k = 0; k < 10; k++) {
// 如果桶中有数据,就取出
if (bucketElementCounts[k] != 0) {
// 循环该桶,放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
// 取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
}
System.out.println("第1轮,对个位的排序处理 arr=" + Arrays.toString(arr));
// 第2轮(对元素的十位进行排序)
bucketElementCounts = new int[10];
for (int j = 0; j < arr.length; j++) {
// 取出元素的个位的值
int num = arr[j] / 10 % 10;
// 放入到对应的桶中
bucket[num][bucketElementCounts[num]] = arr[j];
// 后移一位用来放下次进来的数
bucketElementCounts[num]++;
}
index = 0;
// 遍历每一个桶,从数组中依次取出数据,放入原来的数组
for (int k = 0; k < 10; k++) {
// 如果桶中有数据,就取出
if (bucketElementCounts[k] != 0) {
// 循环该桶,放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
// 取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
}
System.out.println("第2轮,对十位的排序处理 arr=" + Arrays.toString(arr));
// 第3轮(对元素的十位进行排序)
bucketElementCounts = new int[10];
for (int j = 0; j < arr.length; j++) {
// 取出元素的个位的值
int num = arr[j] / 100 % 10;
// 放入到对应的桶中
bucket[num][bucketElementCounts[num]] = arr[j];
// 后移一位用来放下次进来的数
bucketElementCounts[num]++;
}
index = 0;
// 遍历每一个桶,从数组中依次取出数据,放入原来的数组
for (int k = 0; k < 10; k++) {
// 如果桶中有数据,就取出
if (bucketElementCounts[k] != 0) {
// 循环该桶,放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
// 取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
}
System.out.println("第3轮,对百位的排序处理 arr=" + Arrays.toString(arr));
}
/**
* 根据上面推导过程得出规律
*
* @param 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];
}
}
// 得到最大数的位数
int maxLength = (max + "").length();
int[][] bucket = new int[10][arr.length];
int[] bucketElementCounts;
for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
bucketElementCounts = new int[10];
for (int j = 0; j < arr.length; j++) {
// 取出元素的个位的值
int num = arr[j] / n % 10;
// 放入到对应的桶中
bucket[num][bucketElementCounts[num]] = arr[j];
// 后移一位用来放下次进来的数
bucketElementCounts[num]++;
}
int index = 0;
// 遍历每一个桶,从数组中依次取出数据,放入原来的数组
for (int k = 0; k < 10; k++) {
// 如果桶中有数据,就取出
if (bucketElementCounts[k] != 0) {
// 循环该桶,放入
for (int l = 0; l < bucketElementCounts[k]; l++) {
// 取出元素放入到arr
arr[index++] = bucket[k][l];
}
}
}
}
}
}
- 打印结果
第1轮,对个位的排序处理 arr=[542, 53, 3, 14, 214, 748]
第2轮,对十位的排序处理 arr=[3, 14, 214, 542, 748, 53]
第3轮,对百位的排序处理 arr=[3, 14, 53, 214, 542, 748]
排序前[53, 3, 542, 748, 14, 214]
排序后[3, 14, 53, 214, 542, 748]
基数排序的说明:
- 基数排序是对传统桶排序的扩展,速度很快.
- 基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成 OutOfMemoryError 。
- 基数排序时稳定的。[注:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的]