学习排序算法之计数排序

353 阅读4分钟

这是我参与8月更文挑战的第7天,活动详情查看:8月更文挑战

时间复杂度 O(n) 的排序算法

时间复杂度 O(n)

又叫做,线性排序

下面三个排序算法是,不基于比较的排序算法,不涉及元素之间的比较

对要排序的数据要求很苛刻,需要适用场景

计数排序 VS 桶排序 VS 基数排序

  • 计数排序:每个桶只存单一键值(三种排序里面面试问的最多的还是计数排序)
  • 桶排序: 每个桶存储一定范围的数值
  • 基数排序:根据键值的每位数字来分配桶

计数排序

  • 计数排序,不是基于比较的排序算法;
  • 核心是将输入的数据值转化为键存储在额外开辟的数组空间中
  • 是一种拿空间换时间的算法
  • 计数排序只能用在数据范围不大的场景中,如果数据范围K要比排序的数据n大很多,就不适用计数排序了
  • 要转化成非负整数
  • 时间复杂度:O(n+k)
  • 空间复杂度:O(n+k)
原理
  1. 先查找数组里最大的那个值;
  2. 根据(最大的值+1)创建新的数组,用于将数据值转化为键存储,下标是[0,...,maxValue]
  3. 存储:计算每个元素的个数,进行存储,这样就得到,以旧数组的值为下标,相同值的个数为值的新数组
  4. 这里有个很巧妙的地方,就是bucket这个数组,相当于根据下标值排完序了
  5. 直接从小到大进行遍历
// n 代表数组的长度
function countSort (arr, n) {
    if (arr.length <= 1) return arr;
    // 先查找数组里最大的那个值;
    let maxValue = arr[0];
    for (let i = 1; i < n; i++) {
        if (arr[i] >= maxValue) {
            maxValue = arr[i];
        }
    }

    // 创建新的数组,用于将数据值转化为键存储
    // 下标是[0,...,maxValue]
    let bucket = new Array(maxValue + 1);

    // 计算每个元素的个数,进行存储
    // 这样就得到,以旧数组的值为下标,相同值的个数为值的新数组
    for (let i =0; i < n; i++) {
        if (!bucket[arr[i]]) {
            bucket[arr[i]] = 0;
        }
        bucket[arr[i]]++;
    }
    // PS: 这里有个很巧妙的地方,就是bucket这个数组,相当于根据下标值排完序了
    // 所以,直接从小到大进行遍历就好了
    // 对这个新数组进行遍历
    let index = 0;
    for (let j = 0; j < maxValue + 1; j++) {
        // 当bucket有值的时候,赋给arr
        while (bucket[j] > 0) {
            arr[index++] = j;
            bucket[j]--;
        }
    }

}

桶排序

Bucket sort,桶排序是计数排序的升级

  • 时间复杂度:O(n)
  • 空间复杂度:O(n)

原理

将要排序的数据,分到几个有序的桶里,每个桶里的数据再进行单独的排序。

桶内的排序完了,之后,再把每个桶里的数据依次的取出来,组成的序列就是有序的了。

桶排序比较适合用在外部排序之中。就是数据存储在外部磁盘当中,

数据量比较大,内存有限,无法将数据全部加载到内存中

步骤

  1. 遍历整个数组,找到最小的那个值,和最大的那个值
  2. 判断多个桶,每个桶有一个范围,
  3. 遍历数组,把数据放到每个桶里,
  4. 桶里的数据进行单独排序
  5. 最后依次取出,就完成桶排序了

基数排序

基数排序对要排序的数据是有要求的,需要分割出独立的位来比较,而且位之间有递进的关系,

如果a数据的高位比b数据大,那么剩下的低位就不用比较了

每一位的数据范围不能太大,要可以用线性排序算法来排序,

否则基数排序的时间复杂度就无法做到O(n)了

  • 非比较排序
  • 本质上是多关键字排序
  • 桶思想的一种

算法思想

假如有一个数组[123,321,432,654],先取每个元素的个位数,进行计数排序,

排完之后,对新数组再继续用十位数排序,最后,给百位数进行计数排序,这样最后就得到排好的数组了

//LSD Radix Sort
var counter = [];
function radixSort(arr, maxDigit) {
    var mod = 10;
    var dev = 1;
    for (var i = 0; i < maxDigit; i++, dev *= 10, mod *= 10) {
        for(var j = 0; j < arr.length; j++) {
            var bucket = parseInt((arr[j] % mod) / dev);
            if(counter[bucket]==null) {
                counter[bucket] = [];
            }
            counter[bucket].push(arr[j]);
        }
        var pos = 0;
        for(var j = 0; j < counter.length; j++) {
            var value = null;
            if(counter[j]!=null) {
                while ((value = counter[j].shift()) != null) {
                      arr[pos++] = value;
                }
          }
        }
    }
    return arr;
}