基数排序

一、原理

       ☆思想:对输入序列按位分别进行桶排序,桶个数为10,即数字[0-9],分桶时相应位的数字即为桶索引,有 LSD (Least sgnificant digital) 和 MSD (Most sgnificant digital)两种方式。LSD 的排序方式由键值的最右边开始,而 MSD 则相反,由键值的最左边开始。

       ☆过程

        以 LSD 为例,按个位把输入序列放到十个桶内,桶内元素个位都一样,不用排序,再把所有桶合并,此时所有元素的个位是有序的,依次处理百位、千位……,直到最高  位,最终得到有序数组。

        以 MSD 为例,使用数组嵌套实现桶,分两步:

  1. 按最高位把输入序列放到十个桶内,桶内元素最高位都一样,不用排序,位数减1,对长度大于1的数组递归分桶……,直到位数为0,依次处理最高位、次高位……,直到个位,此时表示桶的数组层层嵌套;
  2. 从最外层数组开始合并,最终得到单层有序数组。

二、实现代码

      JavaScript 代码实现一,LSD:

function RadixSort(arr) {
    var brr = [];
    var index, maxValue, maxDigits, baseVal;
    var sortedIndex = 0,
    n = arr.length;

    //获取最大、最小元素
    maxValue = arr[0];
    for (var i = 0; i < n; i++) {
        if (maxValue < arr[i]) {
            maxValue = arr[i];
        }
    }
    //最大值有几位
    maxDigits = 0;
    while (Math.floor(maxValue) && (++maxDigits)) {
        maxValue /= 10;
    }

    //按位数从右到左排序
    for (var start = 1; start <= maxDigits; start++) {
        //当前位基数
        baseVal = Math.pow(10, start - 1);
        //清空临时桶
        brr = [];
        //按当前基数放入桶内
        for (var i = 0; i < n; i++) {
            index = Math.floor(arr[i] / baseVal) % 10;
            if (!brr[index]) {
                brr[index] = [arr[i]];
            } else if (Array.isArray(brr[index])) {
                brr[index].push(arr[i]);
            }
        }

        //把排序结果复制到输入数组
        sortedIndex = 0;
        for (var i = 0; i < brr.length; i++) {
            if (Array.isArray(brr[i])) {
                for (var j = 0; j < brr[i].length; j++) {
                    arr[sortedIndex++] = brr[i][j];
                }
            }
        }
    }
}

       JavaScript 代码实现二,MSD,一般用一个数组表示所有桶,用计数获取数组下标,此处使用数组嵌套实现桶,相对麻烦,性能可能不高,好在容易理解:

function RadixSort(arr) {
    var n = arr.length;
    var brr = [];

    //获取最大元素
    maxValue = arr[0];
    for (var i = 0; i < n; i++) {
        if (maxValue < arr[i]) {
            maxValue = arr[i];
        }
    }
    //最大值有几位
    maxDigits = 0;
    while (Math.floor(maxValue) && (++maxDigits)) {
        maxValue /= 10;
    }
    //递归分桶
    brr = splitBucket(arr, maxDigits);
    //把桶内元素合并为单层数组
    return mergeBucket(brr);
}

//对数组按右数第d位分桶
function splitBucket(arr, d) {
    var brr = [];
    //输入无效退出
    if (d < 1 || arr.length < 2) return;

    //当前位基数
    baseVal = Math.pow(10, d - 1);
    console.log(baseVal);

    //按当前基数放入桶内
    for (var j = 0; j < arr.length; j++) {
        index = Math.floor(arr[j] / baseVal) % 10;
        //console.log(index);
        //空桶,转为数组加入
        if (!brr[index]) {
            brr[index] = [arr[j]];
        } else if (Array.isArray(brr[index])) {
            //brr[index].push(arr[j]);
            //加入数组最后
            brr[index][brr[index].length] = arr[j];
        }
    }
    console.log('div:' + brr);
    //位数递减
    --d;

    //遍历数组,对其元素分桶
    for (var j = 0; d >= 1 && j < brr.length; j++) {
        if (brr[j]&&brr[j].length > 1) {
                console.log('before:' + brr[j]);
                brr[j] = splitBucket(brr[j], d);
                console.log('after:' + brr[j]);
        }
    }
    //返回分桶后数组 
    return brr;
}

//数组扁平化,2020-01-16
function mergeBucket(arr) {
    var sortedIndex = 0;
    var zrr = [];
    var isarray = true;

    while (isarray) {
        sortedIndex = 0;
        zrr = [];
        isarray = false;
        for (var i = 0; i < arr.length; i++) {
            //元素为数组
            if (Array.isArray(arr[i])) {
                isarray = true;
                
                //汇总所有子元素
                for (var j = 0; j < arr[i].length; j++) {
                    zrr[sortedIndex++] = arr[i][j];
                }
            } else if (arr[i]) {
                zrr[sortedIndex++] = arr[i];
            }
        }
        arr = zrr;
    }
    return arr;
}

 

三、优化

       无;

四、复杂度

名称 时间复杂度 空间复杂度  稳定性 
平均 最坏 最优
基数排序 O(n*d) O(n*d) O(n*d)  O(n+k)

      最坏情况,所有元素分到一个桶内,时间复杂度取决于桶内排序使用的算法;

      最优情况,桶大小为1,即只有一个值,复杂度为 O(n*d),d为元素最大值位数;

      本排序需要申请的额外空间取决于输入内容是数字还是字母,或者二者混合,k为桶的数量或区间长度,数字为10,字母为52,混合为62;

      相同元素不会交换,本排序稳定。


参考资料:

posted @ 2019-11-26 10:23  老余的水壶  阅读(218)  评论(0)    收藏  举报