计数排序是一个非基于比较的[排序算法],该算法于1954年由 Harold H. Seward 提出。
它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),
快于任何比较排序算法。
[1] 当然这是一种牺牲空间换取时间的做法,
而且当O(k)>O(n\*log(n))的时候其效率反而不如基于比较的排序
(基于比较的排序的时间复杂度在理论上的下限是O(n\*log(n)),
如归并排序,堆排序)
解释
以上是 百度百科 (最近无法科学上网..)关于 记数排序
的描述。核心点如下:
- 是一种
非比较
的排序 - 需要一个
累计数组
来记录原数组的信息 - 累计数组的
索引
就是原数组的值
- 累计数组的
值
就是原数组某元素重复出现的次数
非比较排序
就是说 不比较
数组中元素的大小关系
。而是通过记录索引
的方式巧妙的
将原数组进行排序
。这样说比较绕,接下来就分步骤说明:
比如说,现在有一个这样的数组:
const arr = [2,1,2,2,3,8,5,5,9]
如果要对其进行记数排序的话,大概需要这几步:
1⃣️. 定义累计数组 其中length
是原数组的最大值+1
// 原数组最大值
const max = Math.max(...arr);
// 定义一个累计数组 数组长度是最大值+1 元素都是0
// 累计数组第一位的空位0 所以长度要+1
const _arr = new Array(max + 1).fill(0);
2⃣️. 将原数组中的值
转换为累计数组的索引
将原数组中的值
转换为累计数组的索引
,遇到重复的元素
,就在累计数组中进行累加
。
比如: 在原数组
中的元素1
出现了1
次,那么在累计数组
中索引为1
的元素的值为1
。在原数组
中的元素2
重复出现了3
次,那么在累计数组
中索引为2
的元素的值为3
。在原数组
中的元素5
重复出现了2
次,那么在累计数组
中索引为5
的元素的值为2
。
其他以此类推。此时累计数组就变成了:(其实通过此步,就已经将数组排好序
了,因为值越大 索引越靠后
)
_arr = [0,1,3,1,0,2,0,0,1,1]
3⃣️. 将累计数组
进行累加
,计算最终要回写的位置
为什么要累加呢?仔细想想 累加的是什么? 是值吗?表面上
是累计数组中的值进行累加
,其实本质上
是原数组的索引进行累加
,因为最终要通过该索引确定原数组
中各个元素的位置
。
从累计数组的第2个
元素开始(因为累计数组中的第一个元素是空位0
),它的每一项
等于它前一项
+它本身
的和。这样做的目的是为了计算出
最终要回写
到结果数组的索引
。
for(let i = 1; i < _arr.length; i++){
// 它的每一项 等于 它的前一项 加 它本身的和
_arr[i] = _arr[i-1] + _arr[i];
}
计算完毕后,此时的累计数组就变成了:
[0,1,4,5,5,7,7,7,8,9]
4⃣️.通过最终的累计数组的值
来确定最终结果数组
的每个元素的最终索引
因为通过第三步
,就完整的记录了原数组的值
(累计数组索引
)和原数组的索引
(累计数组值
)。那既然有了索引和值
,一个数组不就确定了吗?
// 定义一个结果数组result_arr
// 数组长度和arr相同 用来保存最后结果
let result_arr = new Array(arr.length)
// 循环原数组
for (let j = 0; j < arr.length; j++) {
const p = _arr[arr[j]] - 1; // 获取最终要回写的索引
_arr[arr[j]]--; // 新的回写位置 以防万一 如果原数组中有相同的大小的元素 那么第二个元素要写入下一个 所以要-1
result_arr[p] = arr[j];
}
JS实现记数排序
/**
* 记数排序
* @param {要排序的数组} arr
*/
function counting_sort(arr) {
// 最大值
const max = Math.max(...arr);
// 累计数组
const _arr = new Array(max + 1).fill(0);
// 结果数组
const result_arr = new Array(arr.length);
// 累计位递增 将arr中的元素数一遍 根据序号+1
arr.forEach((item, i) => _arr[arr[i]]++);
// 累计求和
for (let i = 1; i < _arr.length; i++) {
_arr[i] = _arr[i - 1] + _arr[i];
}
// 最终结果
for (let j = 0; j < arr.length; j++) {
const p = _arr[arr[j]] - 1;
_arr[arr[j]]--;
result_arr[p] = arr[j];
}
return result_arr;
}
测试一下
const arr = [2, 1, 2, 2, 3, 8, 5, 5, 9];
console.log(counting_sort(arr));
/*
[
1, 2, 2, 2, 3,
5, 5, 8, 9
]
*/
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。