排序算法(9)------基数排序

本文介绍了基数排序算法,它采取多关键字比较策略,有先高位后低位和先低位后高位两种排序方式,一般采用后者。文中以日期和数字序列为例说明排序过程,还给出代码及测试结果,最后总结其可达到线性排序时间、需额外空间、稳定性好等特点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

参考文章:https://blog.youkuaiyun.com/ns_code/article/details/20478753

基数排序

基数排序的排序时间也可以达到线性,尤其在k和d(后面介绍该参数)很小的情况下。

基数排序采取的是多关键字比较的策略,且每个关键字对排序的影响不同,根据关键字影响的主次,有两种排序方法

1、先根据影响最大的关键字来排序,而后在该关键字相同的情况下,再根据影响次之的关键字来排序,依此类推,直到最后按照影响最小的关键字排序后,序列有序。我们称这个为先高位后低位。

2、先根据影响最小的关键字来排序,而后再对全部的元素根据影响次小的关键字来排序,依此类推,直到最后按照影响最大的关键字排序后,序列有序。我们称这个为先低位后高位。

这有点抽象,我们用具体的例子来说明。比如,我们希望用三个关键字(年、月、日)来对日期进行排序,按照基数排序的思想,则:

  • 采取第一种方法排序的思路是这样的:先比较年,形成一个按照年有序排列的序列,而后对年相等的日期,在比较月,对月相等的日期,再比较日,最后得到有序序列。

  • 采取第二种方法排序的思路是这样的:先对所有元素按照日排序,再对所有元素按照月排序,最后对所有元素按照年排序,得到有序序列。

我们一般采用第二种方法来进行排序,比如如下的数字序列:
217,125,362,136,733,522

先对个位上的数字进行排序,得到:

362,522,733,125,136,217

再对十位上的数字进行排序,得到:

217,522,125,733,136,362

最后对百位上的数字进行排序,得到:

125,136,217,362,522,733

很明显,高位数字比低位数字对排序的影响大,该方法的正确性很容易证明,这里不再说明。

我们注意到,这里每一步都需要对各个位上的数进行排序。而为了保证基数排序的正确性(稳定性),我们对每个位上的数进行排序时可以选用计数排序。算法导论上给的伪代码太粗略了,直接贴出自己写的完全代码(包括测试代码),如下:

package com.zhumq.sort;

import java.util.Arrays;
import org.junit.Test;
public class BaseSort {
    /*
     * 这里每一步都需要对各个位上的数进行排序。而为了保证基数排序的正确性(稳定性),
     * 我们对每个位上的数进行排序时可以选用计数排序
     */

    /*
     *  在第一种计数排序的实现形式上做了些修改
        计数排序后的顺序为从小到大
        arr[0...len-1]为待排数组,我们这里采用三位数
        brr[0...len-1]为排序后的有序数组
        w[0...len-1]用来保存取出的每一位上的数,其每个元素均是0-k中的一个值
        crr[0...k]保存0...k中每个值出现的次数
     */
    //主要是新增w[]来保存取出的每一位上的数
    public void count_sort(int arr[],int brr[],int w[],int crr[],int len ,int k) {
        int i;
        //数组crr各元素置0,这步是必须的,因为一位计数排序结束后,crr里面是有值的,如果不重新置零,下面放置操作会出现数组越界!
        for(i=0;i<=k;i++) {
            crr[i] = 0;
        }
        //统计数组w中每个元素重复出现的次数
        for(i = 0;i<len;i++) {
            crr[w[i]]++;
        }
        //求数组w中小于等于i的元素个数
        for(i=1;i<=k;i++) {
            crr[i] += crr[i-1];
        }
        //把arr中的元素放在brr中对应的位置上
        for(i=len-1;i>=0;i--) {
            //跟前面计数排序一样
            brr[crr[w[i]]-1] = arr[i];
            crr[w[i]]--;
        }
        //再将brr中的元素依次复制给arr,arr也就有序了
        for(i=0;i<len;i++) {
            arr[i] = brr[i];
        }   
    }

    /*
        基数排序后的顺序为从小到大
        其中参数d为元素的位数
    */
    public void basicsort(int arr[],int brr[],int w[],int crr[],int len,int k ,int d) {
        int i , j , val = 1;
        //从低位到高位依次进行计数排序
        for(i = 1;i<=d;i++) {
            //w[]保存的是arr中每个元素对应位上的数
            //每位上值得范围在0~k之间
            for(j = 0;j<len;j++) {
                w[j] = (arr[j]/val)%10;
            }
            //对当前位进行计数排序
            count_sort(arr,brr,w,crr,len,k);
            val *= 10;
        }
    }

    @Test
    public void test() {
        int arr[] = {217,125,362,136,733,522};
        int len = arr.length;
        int brr[] = new int[len];
        int k = 7;
        int crr[] = new int[k+1];
        int w[] = new int[len];
        int d = 3;
        //第一次出现问题原因:crr[]未置零!
        basicsort(arr, brr, w, crr, len, k, d);
        System.out.println(Arrays.toString(arr));
    }
}

测试结果如下:
这里写图片描述
最后我们同样对基数排序稍微做下总结:
  1、同样不是基于比较的排序,因此可以达到线性排序时间;
  2、同样采取空间换时间的思想,需要额外的辅助空间,但是时间复杂度仅为O(d(n+k))
  3、基数排序的稳定性同样也很好。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值