切换主题
计数排序
基本思想:计数排序是一种非常特殊的桶排序。找出待排序的数组中最大和最小的元素,统计数组中每个元素出现的次数,生成统计数组,遍历统计数组反向填充原数组。
算法过程描述:
- 首先找出无序数组的取值范围为
[min, max]
,此时的偏移量为min
; - 初始化长为
max- min + 1
统计数组cnts
; - 遍历原数组,统计每个元素出现的次数
cnts[num - min]++
; - 遍历统计数组
cnts
,当遍历到的项值大于0时:
1. 向原数组填充值i + min
,下标步进idx++
;
2. 当前项的统计值递减cnts[i]--
。 - 遍历完
cnts
时,完成排序。
不稳定版
JavaScript
const countingSort = A => {
if (A.length < 2) return A;
let [min, max] = [Infinity, -Infinity]
for (const num of A) {
max = num > max ? num : max
min = num < min ? num : min
}
const len = max - min + 1
const cnts = Array(len).fill(0)
// 统计对应元素个数
for (const num of A) {
cnts[num - min]++ // 偏移量为 min
}
for (let i = 0, idx = 0; i < len; i++) {
while (cnts[i]-- > 0) {
A[idx++] = i + min // 偏移量为 min
}
}
}
稳定版
算法过程描述:
- 前缀和数组的每项表示(倒序遍历原数组
len = A.length-1
,len
递减):- 无
min
偏移时:原数组A
中,小于等于A[len]
的元素有cnts[A[len]]
个,即:A[len]
在原数组中相对位置(排序后)应位于下标idx = cnts[A[len]] - 1
(此处-1
是因为数组从下标0
开始); - 有
min
偏移时:原数组A
中,小于等于A[len]
的元素有cnts[A[len] - min]
个,即:A[len]
在原数组中相对位置(排序后)应位于下标idx = cnts[A[len] - min] - 1
。
- 无
- 这里倒序遍历是为了保证算法稳定性:简单来说就是模拟元素的出桶入桶顺序,倒序遍历保证了先入桶的元素先出桶(即队列)。
JavaScript
const countingSort = A => {
let len = A.length
if (len < 2) return A;
let [min, max] = [Infinity, -Infinity]
for (const num of A) {
max = num > max ? num : max
min = num < min ? num : min
}
const cntLen = max - min + 1
const cnts = Array(cntLen).fill(0)
// 统计对应元素个数
for (const num of A) {
cnts[num - min]++ // 偏移量为 min
}
// 计算统计数组前缀和
for (let i = 1; i < cntLen; i++) {
cnts[i] += cnts[i - 1]
}
// 倒序遍历原数组,从统计数组中找到位置,填入结果数组
const ret = Array(len).fill(0)
while (--len >= 0) {
ret[--cnts[A[len] - min]] = A[len]
}
return ret
}
算法应用
场景
- 如何快速得知高考排名?
- 如何根据年龄给100万用户排序 ?
- 剑指 Offer II 075. 数组的相对排序