数据结构(Java):排序算法之基数排序(又称桶排序)

基数排序(又称桶子法):

基本思想:将所有待比较数值(正整数)统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后,数列就变成一个有序序列。

基数排序是一种非比较型整数排序算法,其原理是将整数按位数切割成不同的数字,然后按每个位数分别比较。由于整数也可以表达字符串(比如名字或日期)和特定格式的浮点数,所以基数排序也不是只能使用于整数。

计数排序的原理:就是用一个数组来统计每种数字出现的次数,然后按照大小顺序将其依次放回原数组,达成排序的目的。

但是计数排序有一个很严重的问题,就是其只能对整数进行排序,一旦遇到字符串时,就无能为力了。

为了弥补上述的缺点,针对于字符串的基数排序诞生了。
基数排序在计数排序的基础上进行了改进,将排序工作拆分为多个阶段,每一个阶段只根据一个字符进行排序,一共排序K轮(K为数据长度)

基数排序是属于稳定性的排序,基数排序是效率高且稳定的

基数排序是桶排序的扩展

基数排序(桶排序):

适合于有不同位数的大小数字,例如以下数列:
在这里插入图片描述
核心思想是:
属于分配式排序,先找十个桶:0~9,每一个数字是一个桶子
第一轮按照元素的个位数排序
桶内分别存放上述数组元素的个位数,按照数组元素的顺序依次存放

之后,按照从左向右,从上到下的顺序(这样是保证排列有序)依次取出元素,组成新的数组。 

 在新的数组中,进行第二轮,按照十位数排序,依次存放于桶中:

​按照从左向右,从上到下的顺序(这样是保证排列有序)依次取出元素,再次组成新的数组。 
进行第三轮,按照百位数排序:

 

 将百位数的元素取出之后,我们发现新的数组已经变成了有序数组

代码实现:

为什么不能用一维数组,一定要用二维数组这样的类似桶的结构来存储中间位排序结果?其实之所以要写这个问题,是因为我觉得这个问题是理解基数排序的关键。基数排序本身原理很简单,但是实现中有两个问题需要考虑:1.怎么保留前一位的排序结果,这个问题用之前提到的排序稳定性可以解决。2.怎么关联该位排序结果和原数组元素,二维数组正是为了解决这个问题使用的办法。在计数排序里,虽然保留了所有相等的元素的相对位置,但是这些相等的元素在计数排序里实际是没有差别的,因此我们可以只保存数组里有多少个这样的元素即可。而基数排序里不同,有些元素虽然在某一位上相同,但是他们其他位上很可能不同,如果只保存该位上有多少个5或者多少个6,那关于元素其他位的信息就都丢弃了,这样也就没法对这些元素更高位进行排序了。

public class RadixSort {
    private static void radixSort(int[] array,int d)
    {
        int n=1;//代表位数对应的数:个1,十10,百100...
        int k=0;//保存每一位排序后的结果用于下一位的排序输入
        int length=array.length;//获取形参传入数组的长度
        //这里用的二维数组
        int[][] bucket=new int[10][length];//排序桶用于保存每次排序后的结果,这一位上排序结果相同的数字放在同一个桶里
        int[] order=new int[length];//用于保存每个桶里有多少个数字
        while(n<d)
        {
            //这里面需要想象力想象存储过程,小伙伴们可以自己在草稿本上画一下
            for(int num:array) //将数组array里的每个数字放在相应的桶里
            {
                int digit=(num/n)%10; //第一轮获每个数的取个位,第二轮十位数字,第三轮千位数字.....
                bucket[digit][order[digit]]=num;  //digit:横排从左到右,从大到小第几个桶子  order[digit]:列,桶子里存储的数
                order[digit]++;//桶子移到下一个位置为存储下一个数字做准备
            }
            for(int i=0;i<length;i++)//从第一个桶子开始查找,将前一个循环生成的桶(就是二维数组内对应列里的值)里的数据覆盖到原数组中用于保存这一位的排序结果
            {
                if(order[i]!=0)//这个桶里有数据,从上到下遍历这个桶并将数据保存到原数组中
                {
                    for(int j=0;j<order[i];j++)
                    {
                        array[k]=bucket[i][j];//将二维数组里面存放的数值保存到原数组
                        k++;
                    }
                }
                order[i]=0;//将桶里计数器置0,用于下一次位排序
            }
            n*=10;
            k=0;//将k置0,用于下一轮保存位排序结果
        }

    }
    public static void main(String[] args)
    {
        int[] A=new int[]{73,22, 93, 43, 55, 14, 28, 65, 39, 81};
        radixSort(A, 100);
        for(int num:A)
        {
            System.out.print(num+" ");
        }
    }
}

算法分析

1、基数排序的性能

其中,d代表数组元素最高为位数,n代表元素个数。

2、时间复杂度

这个时间复杂度比较好计算:count * length;其中 count 为数组元素最高位数,length为元素个数;所以时间复杂度:O(n * d)

3、空间复杂度

空间复杂度是使用了两个临时的数组:10 + length;所以空间复杂度:O(n)。

4、算法稳定性

在基数排序过程中,每次都是将当前位数上相同数值的元素统一“装桶”,并不需要交换位置。所以基数排序是稳定的算法。 

  • 4
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值