js数据结构与算法_11_计数排序

一、认识计数排序

这个排序我是真心喜欢,它帮助我打破了我的思维禁锢,以前我认为给序列排序是一定要在元素之间比较的,而且排序算法的时间复杂度的下限就是O(nlogn),比如我们熟知的快排、希尔、堆排序等等;

计数排序在某些情况下是快过上面说的比较排序算法的,比如0-100排序,计数排序是最好的;

当输入的元素是n个0~k之间的整数时,它的运行时间是O(n+k);

计数排序的核心思想就是用统计数组的下标来表示原始数组的值,而统计数组对应下标的值就是原始数组中值出现的次数;排序就只需要遍历统计数组,按照值的大小来输出下标即可;

二、非稳定计数排序

如果统计数组中的值是大于1的,那么怎样区分这两个相同值的顺序呢?可以区分那说明算法是稳定的,不能区分就说明算法不稳定;话不多说,先上第一个版本的:

function countSort(array) {
  const C = [];
  // 1.计数
  for (let i = 0; i < array.length; i++) {
    const j = array[i];
    C[j] >= 1 ? C[j]++ : (C[j] = 1);
  }
  console.log("统计数组", C);
  const D = [];
  // 2.将下标加入到结果数组(不稳定)
  for (let j = 0; j < C.length; j++) {
    if (C[j]) {
      while (C[j] > 0) {
        D.push(j);
        C[j]--;
      }
    }
  }
  return D;
}

这个算法的逻辑并不复杂,统计数组的长度直接取决于原始数组的最大值,为原始数组最大值+1;这会导致0~最小值之间的位置都浪费掉,等下讲解决方法;拿到统计数组后,直接从前往后遍历统计数组,有就将值加入结果数组;

但缺点也很明显,空间的浪费,以及不是稳定的;

三、改良版计数排序

像[70, 72, 73, 74, 90]这样的数组,用前面的算法的话,70之前不都得空着吗,多浪费呀!

我们可以先或取原始数组中的最大值以及最小值,统计数组的长度就等于最大值-最小值+1;

而最小值即为偏移量,统计数组的索引+偏移量 就是原始数组里面的值了;

let countArray = new Array(max - min + 1).fill(0);

这里填步填充0都可以,我是为了打印美观才填充的;

最后是解决稳定性的问题,我们可以采用对统计数组中所有的计数累加,就是每一项是本身和前一项之和(第一个元素除外),这样的好处就是知道自己的顺序,因为增加的数就代表比自己小的元素的个数;最后我们反向遍历原始数组,依次按照统计数组记录的值输出即可;

function countingSort(array) {
  let length = array.length;
  let resultArray = [];
  let min = array[0];
  let max = array[0];

  // 1.max min
  for (let i = 0; i < length; i++) {
    min = min <= array[i] ? min : array[i];
    max = max >= array[i] ? max : array[i];
  }
  // 统计数组的长度为max-min+1;min作为偏移量

  let countArray = new Array(max - min + 1).fill(0);
  // 2.统计次数
  for (let i = 0; i < length; i++) {
    countArray[array[i] - min] = countArray[array[i] - min]
      ? countArray[array[i] - min] + 1
      : 1;
  }
  // 3.累加计数
  for (let i = 0; i < countArray.length - 1; i++) {
    countArray[i + 1] = (countArray[i + 1] || 0) + (countArray[i] || 0);
  }
  console.log("统计数组:", countArray);
  // 4.反向遍历
  for (let i = length - 1; i >= 0; i--) {
    resultArray[countArray[array[i] - min] - 1] = array[i];
    countArray[array[i] - min]--;
  }
  return resultArray;
}

这里面比较难的代码应该是下面这个了:

 resultArray[countArray[array[i] - min] - 1] = array[i];

我们先来分析一下,统计数组里面值表示了对应的索引在结果数组里对应的位置,如值为5,说明其排第5,那对应在结果数组中的索引是多少?当然是5-1了

所以countArray[array[i] - min] - 1是为了拿到索引;

我们上上面也分析了统计数组的索引+偏移量就是原始数组里面的值,那array[i] - min就是为了拿到统计数组的索引,拿到了索引才能拿到顺序,拿到了顺序我们才能加入正确的值;

这个改良版是我自己写的,还不够浓缩,所以可能有些地方看着比较赘余,欢迎各位提出建议!

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值