计数排序
一、原理
☆思想:对输入数据有要求,要在一定区间内,如课程成绩在[0,100]、人的年龄在[0,500]等;申请一个额外空间,长度为区间长度,遍历输入序列,统计区间中每个索引值的出现次数,根据统计次数把区间索引值复制到输入序列指定位置;
☆过程:以递增为例,输入序列用数组arr表示,长度为n,遍历数组arr,获取数组最大值max和最小值min;申请一个辅助数组brr,长度m为max-min+1;遍历数组arr,每个元素值arr[i]对应brr的一个索引值,就把brr[arr[i]]加1,最终使brr元素值为每个索引在数组arr的出现次数,后续过程有两种方法;
- 方法一:倒序遍历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得到有序数组;
- 方法二:从第二个元素开始遍历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为区间长度;
相同元素不会交换,本排序稳定。
参考资料:

浙公网安备 33010602011771号