基数排序解析及代码实现

6.9 基数排序

6.9.1 基数排序介绍
  • 基数排序(radix sort)属于“分配式排序”(distribution sort),又称“桶子法”(bucket sort),顾名思义,它是通过键值的各个位的值,将要排序的元素分配至某些“桶”中,达到排序的作用基数排序(Radix Sort)是桶排序的扩展
  • 基数排序法是属于稳定性的排序,基数排序法的是效率高的稳定性排序法
6.9.2 基本思想

对于一组待排序数组,将所有数值统一为长度一样的数位,数位较少者高位补零。然后从最低位依次开始,每次取出待排序数的每一位值,并将该位数对应的数值存入到对应的桶中,随后遍历每个桶,将数据顺序放回至数组中,重复上述过程,直到最高位,数列就可以成为一个有序数组。

以数组:{12,33,11,42,189,174,325,456,37,75,88}为例,下面演示基数排序的流程:

第一次循环,遍历元素,找到每个元素的个位数字,并将元素值对应存放到桶中,随后遍历每个桶,将桶中元素依次放入到原数组中:

在这里插入图片描述

第二次循环,遍历数组的元素,找到每个元素的十位数,并将元素值对应存放到桶中,随后遍历每个桶,将桶中元素依次放入到原数组中:

在这里插入图片描述

第三轮:遍历数组的元素,找到每个元素的百位数字,并将元素值对应存放到桶中,随后遍历每个桶,将桶中元素依次放入到原数组中:

在这里插入图片描述

由于整个数组中最大的元素为百位数字,因此只需遍历三次,找到百位即可。

从上述过程中可以发现,在一些情况中,可能有些桶中元素一直为0,其他桶可能会出现数据集中现象。为了保证桶中数据不溢出,在初始化时,需要定义每个桶的初始容量均为数组长度大小。总共需要10个桶。而循环遍历的次数为整个数组中最大元素的有效位数次;

因此可以发现,整个过程的时间复杂度没有很高,但当数据量较大时,会有极高的内存开销,这就是典型的以空间换时间的排序算法。

6.9.3 代码实现细节
package com.kevin.sortAlgorithm;

import java.util.Arrays;

/**
 * @author : kevin ding
 * @date : 2022/3/7 22:52
 * @description : 基数排序思想:
 				初始设置编号从0到9的10个桶,将每个数的个位数取出,判断其值,并将数放到对应的桶中。存放完毕顺序将每个桶中的元素依次取出,放到原数组中;第二次,将数组中每个数的十位数,取出并将数放到对应编号的桶中,存放完毕后将每个桶中的元素取出,放回原数组;依次执行
 */
public class RadixSortDemo {
    public static void main(String[] args) {
        int[] array = {12,33,11,42,189,174,325,456,37,75,88};
        radixSortAnalysis(array);
    }

    public static void radixSortAnalysis(int[] array){
        // 定义一个二维数组,表示10个桶,其中每个桶就是一个一维数组
        // 为了防止桶中数据溢出,每个一维数组(桶)大小定为array.length
        int[][] bucket = new int[10][array.length];

        // 为了方便记录每个桶中存放了多少数,定义一个一维数组,每个位置的值表示下标对应的桶中存放的数值个数
        // elementCount[1] 记录的是bucket[1]这个桶中存放数据的个数
        int[] elementCounts = new int[10];

        // 第1轮处理  取出元素的个位数,放置到对应的桶中
        for(int j = 0; j<array.length; j++){
            // 取出每个元素的个位元素 数值
            int digitValue = array[j] /1 % 10;

            // 放入到对应的桶中
            bucket[digitValue][elementCounts[digitValue]] = array[j];
            // 每次放入完之后,元素个数加 1
            elementCounts[digitValue] += 1;
        }
        // array遍历完之后,需要按照桶的顺序取出数据,放入到原数组中
        int index = 0;
        // 遍历每一个桶中的元素,存放到数组中
        for (int j = 0; j < elementCounts.length; j++) {
            // 如果该桶中数据个数不为0,则遍历并将元素放入到原数组array中
            if(elementCounts[j] != 0){
                // 循环遍历该桶
                for (int k = 0; k < elementCounts[j]; k++) {
                    // 取出元素 放入到原数组中
                    array[index] = bucket[j][k];
                    // 随后将index后移
                    index += 1;
                }
            }
            // 在每次处理完,需要将每个elementCounts的计数置为0
            elementCounts[j] = 0;
        }
        System.out.println("第1轮处理过后,array:" + Arrays.toString(array));
        System.out.println("=-=-==============================");

        // 第2轮处理  取出元素的十位数,放置到对应的桶中
        for(int j = 0; j<array.length; j++){
            // 取出每个元素的个位元素 数值
            int digitValue = array[j] /10 % 10;

            // 放入到对应的桶中
            bucket[digitValue][elementCounts[digitValue]] = array[j];
            // 每次放入完之后,元素个数加 1
            elementCounts[digitValue] += 1;
        }
        // array遍历完之后,需要按照桶的顺序取出数据,放入到原数组中
        index = 0;
        // 遍历每一个桶中的元素,存放到数组中
        for (int j = 0; j < elementCounts.length; j++) {
            // 如果该桶中数据个数不为0,则遍历并将元素放入到原数组array中
            if(elementCounts[j] != 0){
                // 循环遍历该桶
                for (int k = 0; k < elementCounts[j]; k++) {
                    // 取出元素 放入到原数组中
                    array[index] = bucket[j][k];
                    // 随后将index后移
                    index += 1;
                }
            }
            // 在每次处理完,需要将每个elementCounts的计数置为0
            elementCounts[j] = 0;
        }
        System.out.println("第2轮处理过后,array:" + Arrays.toString(array));
        System.out.println("=-=-==============================");

        // 第3轮处理  取出元素的百位数,放置到对应的桶中
        for(int j = 0; j<array.length; j++){
            // 取出每个元素的个位元素 数值
            int digitValue = array[j] /100 % 10;

            // 放入到对应的桶中
            bucket[digitValue][elementCounts[digitValue]] = array[j];
            // 每次放入完之后,元素个数加 1
            elementCounts[digitValue] += 1;
        }
        // array遍历完之后,需要按照桶的顺序取出数据,放入到原数组中
        index = 0;
        // 遍历每一个桶中的元素,存放到数组中
        for (int j = 0; j < elementCounts.length; j++) {
            // 如果该桶中数据个数不为0,则遍历并将元素放入到原数组array中
            if(elementCounts[j] != 0){
                // 循环遍历该桶
                for (int k = 0; k < elementCounts[j]; k++) {
                    // 取出元素 放入到原数组中
                    array[index] = bucket[j][k];
                    // 随后将index后移
                    index += 1;
                }
            }
            // 在每次处理完,需要将每个elementCounts的计数置为0
            elementCounts[j] = 0;
        }
        System.out.println("第3轮处理过后,array:" + Arrays.toString(array));
    }
}
6.9.4 基数排序
public static void radixSort(int[] array){
    // 需要定义一个最大值变量,记录最大元素,获取其位数
    int maxValue = array[0];
    for (int ele : array) {
        if(maxValue <= ele){
            maxValue = ele;
        }
    }
    // 最大值的位数
    int maxLength = (maxValue + "").length();
    // 定义一个二维数组,表示10个桶,其中每个桶就是一个一维数组
    // 为了防止桶中数据溢出,每个一维数组(桶)大小定为array.length
    int[][] bucket = new int[10][array.length];

    // 为了方便记录每个桶中存放了多少数,定义一个一维数组,每个位置的值表示下标对应的桶中存放的数值个数
    // elementCount[1] 记录的是bucket[1]这个桶中存放数据的个数
    int[] elementCounts = new int[10];

    // 需要遍历最大值位数 次,若最大值为百位,需要遍历三次
    for (int i = 0; i < maxLength ; i++) {
        for(int j = 0; j<array.length; j++){
            // 取出每个元素的个位元素 数值
            int digitValue = array[j] /(1* (int) Math.pow(10, i)) % 10;

            // 放入到对应的桶中
            bucket[digitValue][elementCounts[digitValue]] = array[j];
            // 每次放入完之后,元素个数加 1
            elementCounts[digitValue] += 1;
        }
        // array遍历完之后,需要按照桶的顺序取出数据,放入到原数组中
        int index = 0;
        // 遍历每一个桶中的元素,存放到数组中
        for (int j = 0; j < elementCounts.length; j++) {
            // 如果该桶中数据个数不为0,则遍历并将元素放入到原数组array中
            if(elementCounts[j] != 0){
                // 循环遍历该桶
                for (int k = 0; k < elementCounts[j]; k++) {
                    // 取出元素 放入到原数组中
                    array[index] = bucket[j][k];
                    // 随后将index后移
                    index += 1;
                }
            }
            // 在每次处理完,需要将每个elementCounts的计数置为0
            elementCounts[j] = 0;
        }
    }
    System.out.println("基数排序过后,array:" + Arrays.toString(array));
}

总结:

  • 基数排序是对传统桶排序的扩展,速度很快.

  • 上述分析不难发现,基数排序是经典的空间换时间的方式,占用内存很大, 当对海量数据排序时,容易造成OutOfMemoryError

  • 基数排序时稳定的。

  • 排序的稳定性介绍:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值