[路飞]_有趣的排序-计数排序及优化

602 阅读3分钟

「这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战

计数排序B站视频欢迎一键三连

计数排序是一个非基于比较的排序算法,该算法于1954年由 Harold H. Seward 提出。它的优势在于在对一定范围内的整数排序时,它的复杂度为Ο(n+k)(其中k是整数的范围),快于任何比较排序算法。
有趣之处就在于,计数排序不是基于原址排序的,需要“数”出数组中每个元素的个数,然后按照出现的次数依次添加到一个新的数组中。

  • 时间复杂度:扫描一次source,扫描一个helper,复杂度为N+K
  • 空间复杂度:辅助空间 k ,k =maxOf(source) - minOf(source) +1
  • 非原址排序
  • 稳定性:相同元素不会出现交叉,非原址都是拷来考去
  • 计数有缺点,数据密集或者范围小时,适用
  • 而且当 O(k)>O(nlog(n)) 的时候其效率反而不如基于比较的排序(基于比较的排序的时间复杂度在理论上的下限是O(nlog(n)), 如归并排序,堆排序)

动画演示

countingSort.gif

分析

  1. 找到数组中最大的元素
  2. 使用一个数组,长度等于最大值max+1(数字从0开始),记录每个元素出现的次数
  3. 根据计数数组,依次输出到新的数组中

简单版代码实现

const arr = [02537910376]
// 简单版
function countSort(arr) {
  let max = -Infinity // 记录数组中最大的元素
  for (let v of arr) {
    max = Math.max(max, v)
  }
  // 记录每个元素出现次数
  const count = new Array(max + 1).fill(0)
  for (let v of arr) {
    count[v]++
  }
  const res = [] //结果数组
  for (let i = 0; i < count.length; i++) {
    let tmp = count[i]
    while (tmp--) {
      res.push(i)
    }
  }
  return res
}
console.log(countSort(arr)) //[0, 2, 3, 3, 5, 6, 7, 7, 9, 10]

到这里聪明的你可能已经发现问题了,计数数组的长度和数组中的最大值max有关。如果数组中的元素所属区间比较集中,最大值又非常大,将导致计数数组的空间浪费。比如范围在[100,200],计数数组的长度将达到201,[0,99]的空间就全都浪费了。
为了优化,可以再找到数组中的最小值min,则可以将计数数组的长度缩小为max-min+1,输出的时候做相应的移位加上最小值min
这样做还有一个好处,就是可以处理负数数组下标的情况,但是任然会有空间浪费的情况,比如元素区间较大的情况,例如[-101,200]。

优化版本

// 优化空间浪费及有负数情况
const arr2 = [-101109107103108102103110107103]
function countSort_v2(arr) {
  let max = -Infinity, 
    min = Infinity //额外记录最小值
  for (let v of arr) {
    max = Math.max(max, v)
    min = Math.min(min, v)
  }
  const count = new Array(max - min + 1).fill(0//数组长度最大值-最小值,最小值变成下标0,
  // 如果最大值和最小值差距很大,仍然存在空间浪费的情况
  for (let v of arr) {
    count[v - min]++ //需要减去最小值
  }
  const res = [] 
  for (let i = 0; i < count.length; i++) {
    let tmp = count[i]
    while (tmp--) {
      res.push(i + min) //这里要加回来
    }
  }
  return res
}
console.log(countSort_v2(arr2)) //[-101, 102, 103, 103, 103, 107, 107, 108, 109, 110]

希望有大佬能指导如何进一步优化

参考自 https://blog.csdn.net/weixin_44491927/article/details/105120985