计数排序
一般遇到排序我们首先会想比较大小,比如冒泡排序, 插入排序, 合并排序, 快速排序等.排序过程都需要去对比,寻找参照物然后确定顺序位置.
这时候如果有一个参照物,它本身就是有序的,我们只要将对应的元素放置进去,那么完全放置进去后呈现的就是排序好的.
计数排序思想
1.计数排序是利用数组下标作为参照物确定元素位置
2.运用下标作为参照即参与排序的元素可以变成数组下标,那么就要求被排元素必须是整数
计数排序过程
- 描述
1.有一组整数序列 A,找到A中的最大值 max
2.新建累计数组B, B.length 为 max+1 ,这样的话B的最后一个元素下标为max
3.数组B值以0填充,开始计数, A中元素出现一次, 对应的B位置值加一,计算下标值在A中出现的频次
4.把B中每个元素与之前元素进行累加,表示已占用空间,奇妙发生在此,当前占用空间-1即是元素排序后存在的下标位置,然后自身计数-1,因为会有重复数据
4.新建写回数组C, 长度与A一致, 把在B中排序完成集合写回C
- 图解
原生数组A
6 | 2 | 2 | 4 | 3 | 3 | 5 | 1 |
新建数组B, 取A中最大值6加上一为B长度,以零填充
0 | 1 | 2 | 3 | 4 | 5 | 6 |
0 | 0 | 0 | 0 | 0 | 0 | 0 |
数组B对A中的值进行计数
0 | 1 | 2 | 3 | 4 | 5 | 6 |
0 | 1 | 2 | 2 | 1 | 1 | 1 |
B数组元素进行累加,表示已占用位置
0 | 1 | 2 | 3 | 4 | 5 | 6 |
0 | 1 | 3 | 5 | 6 | 7 | 8 |
新建数组C 用于回写排序元素,长度与A一致.
我们先分析几个便于理解
- B[0]=0,已占据0空间, 即A中不存在这个元素
- B[1]=1,已占据一个空间即 1-1=0 C[0]=1
以上是根据B的下标顺序来分析的,回写我们需要根据A中元素,A中第一个为6,在B中对应下标6,值为8,表示已占据8个空间,回写C[8-1] = 6
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
1(8) | 2(3) | 2(2) | 3(6) | 3(5) | 4(4) | 5(7) | 6(1) |
注释:值后小括号表示回写顺序
- 代码实现
function counting_sort(A){
const max = Math.max(...A)
let B = Array.from({length:max+1}).fill(0); //创建累计数组,并填充0
let C = new Array(); //创建回写数组
A.forEach( (_,i) => B[A[i]]++) //累计数组频次计算
for(let i=1; i<B.length; i++){
B[i] = B[i-1]+B[i] //累计数组占位累计
}
for(let i=0; i<A.length; i++){
const p = B[A[i]]-1; //记录回写位置
B[A[i]]-- //新回写位置
C[p] = A[i]
}
return C
}