最大位长不确定的基数排序及Java实现

基数排序的基本思想我就不赘述了,百度百科有。另外,一大堆博客也提到这个算法。但是我发现很多都只提到LSD,有少数提到MSD的实现。这些实现中,几乎在调用自己实现的LSD/MSD实现时,还要手动指定最大位长度。
我觉得这有点搞笑,万一在某种情况下,我们并不知道要排序的输入数组中的值呢?如果我们并不知道这个数组是什么样子的,只知道要对它进行排序,那更不可能知道最大位长度是多少。而且,如果待排序的元素很多,也许压根就看不出最大位长度。
本人有点小强迫症,所以我自己实现了最大位长度不确定情况下的基数排序,下面分别给出LSD/MSD的Java实现。当然,这只是我个人风格的实现,仅供参考。并未经过什么大牛的检验,可能有所疏漏或者有待优化的地方,欢迎反馈交流。

PS:

  • 哨兵。以下实现中,除了LSD的输入数组没有哨兵之外,其它的数组中的首元素都是哨兵,记录当前bucket(桶)的元素的数量。其实我平时很少用哨兵,因为它跟从0开始计数的惯常思维有点出入,但这里出乎意料地有所帮助,所以就用了。当然,也可以使用List,不过随机访问性能比不上数组。

  • 不定位长的情况下,要确定最大位长,直觉思维就是找出最大值,然后计算最大值的位长,就是最大位长了。但是这样需要先浪费O(n)时间,才可以开始算法。本人的想法是,在LSD中,这个最大位长的检测与基数排序可以同时进行,算是一个小trick吧。但是,在MSD中,还是采取直觉思维。因为MSD是从高位开始,实现想不到什么trick可以实现基数排序与最大位长的检测同步进行。

具体实现如下:

package com.thomas.algorithm.sort;

public class RadixSort {
    private static final int RADIX = 10;

    public static void lsd(int[] array)
    {
        if(null == array || array.length < 2)
            return;
        final int len = array.length;
        int[][] bucket = new int[RADIX][len + 1];
        //当前位为0的数字个数
        int cnt = 0;
        for(int i = 1;; i *= 10)
        {
            cnt = 0;
            //根据当前bit,将数组元素分配到对应的桶中
            for(int j = 0; j < len; ++j)
            {
                int bit = (array[j] / i) % 10;
                //哨兵
                int bucketLen = bucket[bit][0];
                bucket[bit][bucketLen + 1] = array[j];
                ++bucket[bit][0];

                //当前位为0,则cnt加1
                if(bit == 0)
                    ++cnt;
            }
            //若某位(假如千位)导致cnt >= len
            //说明所有数都没有千位,则不用再进行基数排序了
            //最外层循环退出
            if(cnt >= len)  break;

            //将桶中元素收集回原数组
            int arrayIndex = 0;
            for(int k = 0; k < RADIX; ++k)
            {
                final int bucketLen = bucket[k][0];
                for(int j = 1; j <= bucketLen; ++j)
                    array[arrayIndex++] = bucket[k][j];
                //哨兵记录的是长度,记得清0
                bucket[k][0] = 0;
            }
        }
    }

    public static void msd(int[] array)
    {
        if(null == array || array.length < 2)
            return;

        final int len = array.length;
        int[] temp = new int[len + 1];
        temp[0] = len;
        System.arraycopy(array, 0, temp, 1, len);

        int max = getMax(array);
        int bitCnt = getBitCount(max);
        msd(temp, bitCnt);
        System.arraycopy(temp, 1, array, 0, len);
    }

    private static void msd(int[] array, int n)
    {
        //当前桶中没有元素
        //或者仅有一个元素,则无须排序
        final int len = array[0];
        if(len < 2)    return;

        int[][] buckets = new int[RADIX][len + 1];
        //分配
        for(int i = 1; i <= len; ++i)
        {
            int element = array[i];
            int bit = getBit(element, n);
            ++buckets[bit][0];
            final int index = buckets[bit][0];
            buckets[bit][index] = element;
        }

        //对各个桶递归分配
        for(int[] bucket : buckets)
            msd(bucket, n - 1);
        //收集
        int cnt = 0;
        for(int i = 0; i < RADIX; ++i)
        {
            final int bucketLen = buckets[i][0];
            for(int k = 1; k <= bucketLen; ++k)
                array[++cnt] = buckets[i][k];
        }
        array[0] = cnt;
    }

    private static int getMax(final int[] array)
    {
        assert(null != array);
        int max = array[0];
        final int len = array.length;
        for(int i = 1; i < len; ++i)
        {
            if(array[i] > max)
                max = array[i];
        }
        return max;
    }

    //计算num的位数
    private static int getBitCount(int num)
    {
        String str = String.valueOf(num);
        return str.length();
    }

    //取num的第bit位
    private static int getBit(int num, int bit)
    {
        String str = String.valueOf(num);
        final int len = str.length();
        if(bit > len)
            return 0;
        return str.charAt(len - bit) - '0';
    }
}

你可能感兴趣的:(java,算法,基数排序)