十大算法排序之计数排序

167 阅读1分钟

Counting Sort,适合少量的非负数排序。可实现优化以支持负数。

非比较排序。

由于用来计数的数组count的长度取决于待排序数组中数据的范围(等于待排序数组的最大值与最小值的差加上1),这使得计数排序对于数据范围很大的数组,需要大量时间和内存。例如:计数排序是用来排序0到100之间的数字的最好的算法,但是它不适合按字母顺序排序人名。

算法步骤

  • 找出待排序的数组中最大和最小的元素
  • 统计数组中每个值为i的元素出现的次数,存入计数数组的第i项
  • 对所有的计数累加
  • 反向填充目标数组

image.png

复杂度

标题数据
平均时间复杂度O(n+k)
最好情况O(n+k)
最坏情况O(n+k)
空间复杂度O(k)
排序方式Out-place
稳定性稳定

代码演示

     function countingSort(list) {
            let len = list.length;
            if (len < 2) return list;
            //设置最大最小默认值
            let min = list[0],
                max = list[0];
            //通过循环比较,确定最大最小值
            for (let i = 0; i < len; i++) {
                if (list[i] > max) {
                    max = list[i]
                } else if (list[i] < min) {
                    min = list[i]
                }
            }
            //设置计数数组,数组空间为最大-最小+1,加少空间浪费
            let count = new Array(max - min + 1)
            //循环把计数的值放进计数数组中
            for (let i = 0; i < len; i++) {
                //元素-最小值得出计数数组的下标
                let idx = list[i] - min;
                if (count[idx] > 0) {
                    count[idx]++ //说明该下标已经存在值,所以+
                } else {
                    count[idx] = 1 //初始化该下标的值
                }
            }
            //设置新书组,将计数数组的值放回,完成排序
            let res = [];
            for (let i = 0; i < count.length; i++) {
                //说明此下标存在值
                while (count[i] > 0) {
                    res = [...res, i + min];
                    count[i]--
                }
            }
            return res
        }

image.png