计数排序

一、原理

       ☆思想:对输入数据有要求,要在一定区间内,如课程成绩在[0,100]、人的年龄在[0,500]等;申请一个额外空间,长度为区间长度,遍历输入序列,统计区间中每个索引值的出现次数,根据统计次数把区间索引值复制到输入序列指定位置;

       ☆过程:以递增为例,输入序列用数组arr表示,长度为n,遍历数组arr,获取数组最大值max和最小值min;申请一个辅助数组brr,长度m为max-min+1遍历数组arr,每个元素值arr[i]对应brr的一个索引值,就把brr[arr[i]]加1,最终使brr元素值为每个索引在数组arr的出现次数,后续过程有两种方法;

  1. 方法一:倒序遍历brr,同时循环判断brr[j]是否大于0,比如如果brr[m-1]大于0,把max赋给arr[n-1],同时brr[m-1]减1、arr索引减1变为n-2;如果brr[m-1]仍大于0,把max赋给arr[n-2],同时brr[m-1]减1、arr索引减1变为n-3;反复循环直到brr[m-1]为0,结束循环;brr其余元素仍这样循环处理,最终arr得到有序数组;
  2. 方法二:从第二个元素开始遍历brr,每个元素值加上前一个元素值,即brr[j] += brr[j - 1],得到每个索引值在arr中的序号,在arr中重复出现的索引值其序号和前一个索引值序号不连续;
  • 申请一个辅助数组crr,长度和arr一样,倒序遍历arr,其元素值arr[k]在brr中索引为pos = arr[k]-min,在arr排序后的索引为index = brr[pos] - 1,把arr[k]赋给crr[index],同时brr[pos]减1;遍历完成后,crr即为有序数组,返回crr或复制到arr即可;
  • 此方法一定要申请辅助数组crr,因为在遍历arr时,如果直接把元素值按其序号更新到arr,可能会覆盖其前元素;

二、实现代码

      JavaScript 代码实现方法一:

function countingSort(arr) {
    var arrMaxVal, arrMinVal;
    var brr;
    //数组长度
    var n = arr.length;
    //arr最后一个元素索引
    var sortedIndex = n-1;

    //获取最大、最小元素
    arrMaxVal = arr[0];
    arrMinVal = arr[0];
    for (var i = 0; i < n; i++) {
        if (arrMaxVal < arr[i]) {
            arrMaxVal = arr[i];
        }
        if (arrMinVal > arr[i]) {
            arrMinVal = arr[i];
        }
    }
    //申请长度为区间长度的数组
    brr = new Array(arrMaxVal - arrMinVal + 1);

    //把元素值作为索引统计出现次数
    for (var i = 0; i < n; i++) {
        if (!brr[arr[i] - arrMinVal]) {
            brr[arr[i] - arrMinVal] = 0;
        }
        brr[arr[i] - arrMinVal]++;
    }

    //倒序遍历brr,保证排序稳定,复制元素到输入数组
    for (var i = brr.length-1; i >=0; i--) {
        for (; brr[i] > 0; brr[i]--) {
            arr[sortedIndex--] = arrMinVal + i;
        }
    }
}

 

      JavaScript 代码实现方法二:

function countingSort(arr) {
    var arrMaxVal, arrMinVal, arrlength;
    var brr, pos;
    //数组长度
    var n = arr.length;
    //申请等长辅助数组,保存排序后元素
    var crr = new Array(n);

    //获取最大、最小元素
    arrMaxVal = arr[0];
    arrMinVal = arr[0];
    for (var i = 0; i < n; i++) {
        if (arrMaxVal < arr[i]) {
            arrMaxVal = arr[i];
        }
        if (arrMinVal > arr[i]) {
            arrMinVal = arr[i];
        }
    }
    //申请长度为区间长度的数组
    brr = new Array(arrMaxVal - arrMinVal + 1);
    //初始化数组为0
    for (var i = 0; i < brr.length; i++) {
        brr[i] = 0;
    }
    //把元素值作为索引统计出现次数
    for (var i = 0; i < n; i++) {
        brr[arr[i] - arrMinVal]++;
    }
    //累计次数得到序号
    for (var i = 1; i < brr.length; i++) {
        brr[i] += brr[i - 1];
    }
    //遍历输入数组,把元素放入目标数组
    //倒序遍历,保证排序稳定,2019-11-30
    for (var i = n-1; i >=0 ; i--) {
        pos = arr[i] - arrMinVal;
        crr[brr[pos] - 1] = arr[i];
        brr[pos]--;
    }
    //复制到输入数组
    for (var i = 0; i < n; i++) {
        arr[i] = crr[i];
    }
}

 

三、优化

       无;

四、复杂度

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

      本排序需要申请n+m额外空间,n为输入序列长度,m为区间长度;

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


参考资料:

posted @ 2019-11-24 18:06  老余的水壶  阅读(151)  评论(0)    收藏  举报