17、数据结构与算法 - 基础:排序算法之基数排序

1、基数排序介绍

基数排序(radix sort)属于 “分配式排序” (distribution sort),又称为 “桶子法”(bucket sort),或者 bin sort,它通过把要排序的元素分配到一些桶中,进行排序。

基数排序属于稳定的排序,基数排序是效率比较高的排序法。基数排序是桶排序的扩展

基数排序主要利用分配和收集两种基本操作,不太适合对字符串和文字等进行排序。

2、基数排序基本思想

  • 将所有待比较的值统一为同样的数位长度,数位短的前面 补零,然后从低位开始依次进行排序,从最低位到最高位排序完成后数列就变为有序数列
  • 基数排序从低位到高位进行,使最后一次计数排序完成后,数组有序,原理在于对待排序的数据,权重未知的情况
  • 先按照权重小的因子排序,然后按照权重大的因子排序,所以基数排序更适合对时间、字符串等这些权重未知的数据进行排序

3、基数排序代码实现

分步代码实现

public static void main(String[] args) {
   
     
    int[] arr = {
   
     23, 2, 12, 54, 829, 45, 78, 767};
    radixSort(arr);
    System.out.println(Arrays.toString(arr));
}

public static void radixSort(int[] arr) {
   
     
    // 第一轮排序对每个元素的个位进行排序
    // 定义一个二维数组,表示从 0 到 9 这 10 个桶,每个桶是一个一位数组。每个一位数组大小定为 arr.length
    int[][] bucket = new int[10][arr.length];

    // 定义个一个一维数组,记录每个桶(也就是 0 ~ 9 这几个桶)存放数据的个数
    int[] bucketElementCounts = new int[10];

    // 第一次循环,先比较个位数
    for (int i = 0; i < arr.length; i++) {
   
     
        // 取出每个元素的个位
        int digitOfElement = arr[i] % 10;
        // 取出的个位放到对应的桶中。比如数组中第 1 元素 23 取出个位 3 应该放到二维数组 bucket[3][bucketElementCounts[3]] 的位置
        bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
        bucketElementCounts[digitOfElement]++;
    }
    int index = 0;
    // 比较完个位数后重新存入原数组
    for (int i = 0; i < bucketElementCounts.length; i++) {
   
     
        // 如果桶中有数据,放进原数组中
        if (bucketElementCounts[i] != 0) {
   
     
            // 循环第 i 个桶中的数据
            for (int j = 0; j < bucketElementCounts[i]; j++) {
   
     
                // 赋值到原数组
                arr[index++] = bucket[i][j];
            }
        }
        // 处理完成后将桶中个数置为 0
        bucketElementCounts[i] = 0;
    }
    // 第一次循环结果:[2, 12, 23, 54, 45, 767, 78, 829]

    // 第二次循环,比较十位数
    for (int i = 0; i < arr.length; i++) {
   
     
        // 取出每个元素的十位
        int digitOfElement = arr[i] / 10 % 10;
        // 取出的十位放到对应的桶中
        bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
        bucketElementCounts[digitOfElement]++;
    }
    index = 0;
    // 比较完十位数后重新存入原数组
    for (int i = 0; i < bucketElementCounts.length; i++) {
   
     
        // 如果桶中有数据,放进原数组中
        if (bucketElementCounts[i] != 0) {
   
     
            // 循环第 i 个桶中的数据
            for (int j = 0; j < bucketElementCounts[i]; j++) {
   
     
                // 赋值到原数组
                arr[index++] = bucket[i][j];
            }
            bucketElementCounts[i] = 0;
        }
    }

    // 第二次循环结果:[2, 12, 23, 829, 45, 54, 767, 78]

    // 第三次循环,比较百位数
    for (int i = 0; i < arr.length; i++) {
   
     
        // 取出每个元素的百位
        int digitOfElement = arr[i] / 100 % 10;
        // 取出的百位放到对应的桶中
        bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[i];
        bucketElementCounts[digitOfElement]++;
    }
    index = 0;
    // 比较完百位数后重新存入原数组
    for (int i = 0; i < bucketElementCounts.length; i++) {
   
     
        // 如果桶中有数据,放进原数组中
        if (bucketElementCounts[i] != 0) {
   
     
            // 循环第 i 个桶中的数据
            for (int j = 0; j < bucketElementCounts[i]; j++) {
   
     
                // 赋值到原数组
                arr[index++] = bucket[i][j];
            }
        }
    }

    // 第三次排序结果:[2, 12, 23, 45, 54, 78, 767, 829] 成为有序数列
}

完整代码实现

public static void main(String[] args) {
   
     
    int[] arr = {
   
     23, 2, 12, 54, 829, 45, 78, 767};
    radixSort(arr);
    System.out.println(Arrays.toString(arr));
}

public static void radixSort(int[] arr) {
   
     
    // 首先循环取得数组中最大数
    int max = arr[0];
    for (int i = 0; i < arr.length; i++) {
   
     
        if (arr[i] > max) {
   
     
            max = arr[i];
        }
    }
    // 最大数是几位数
    int maxLength = (max + "").length();
    for (int i = 0, n = 1; i < maxLength; i++, n *= 10) {
   
     

        // 创建桶,总共 10 个桶,每个桶最多 arr.length 个数
        int[][] bucket = new int[10][arr.length];
        // 记录每个桶中的数据
        int[] bucketElementCounts = new int[10];

        // 遍历数组
        for (int j = 0; j < arr.length; j++) {
   
     
            int digitOfElement = arr[j] / n % 10;
            // 比较后放到 bucket 中
            bucket[digitOfElement][bucketElementCounts[digitOfElement]] = arr[j];
            bucketElementCounts[digitOfElement]++;
        }
        int index = 0;
        for (int j = 0; j < bucketElementCounts.length; j++) {
   
     
            if (bucketElementCounts[j] != 0) {
   
     
                for (int k = 0; k < bucketElementCounts[j]; k++) {
   
     
                    // 赋值到原数组
                    arr[index++] = bucket[j][k];
                }
            }
            bucketElementCounts[j] = 0;
        }
        System.out.printf("第%s轮循环的结果为:%s\n", i + 1, Arrays.toString(arr));
    }
}