计数排序

107 阅读2分钟

持续创作,加速成长!这是我参与「掘金日新计划 · 10 月更文挑战」的第19天

计数排序

计数排序也是桶排序的一种实现思路,理解起来比基数排序更容易,更直观

思想

拿到一个数组,我们找到它的最大值与最小值:

[1, 2, 4, 6, 8, 9, 10, 2, 7] 最大值:10,最小值:1

能确定的是,其他元素的范围都在最大值与最小值之间,即每一项 ∈ [1, 10]

现在我们准备一个大桶(数组),在这个大桶内放许多小桶(二维数组),小桶的编号(序号)就在数组的最小值与最大值之间,这里就创建从1-10

bigBucket = [undefined,[],[],[],[],[],[],[],[],[]]

当然,上面是js的定义方式,其他严格的语言可以直接定义从0到最大值的二维数组

小桶定义好了,我们把所有待排序的元素依次遍历,逐个放入对应的小桶内:

bigBucket[1] = 1

bigBucket[2] = 2

bigBucket[3]没有对应的元素,所以保持为空

bigBucket[4] = 4

bigBucket[6] = 6

bigBucket[8] = 8

bigBucket[9] = 9

bigBucket[10] = 10

bigBucket[2] = [2, 2] //注意这里有重复元素,意味着不能将其写死直接赋值,要向后追加新元素

bigBucket[7] = 7

现在所有元素放到了小桶里,如果我们按序遍历小桶,取出的元素一定也是有序的

从bigBucket中从小到大依次取出,重新赋值给arr:

[1, 2, 2, 4, 6, 7, 8, 9, 10]

实现(javascript)

function countingSort(arr){
    //初始化大桶
    let bigBucket = [];
    for(let item of arr){
        if(bigBucket[item] === undefined){
            bigBucket[item] = [];
        }
        bigBucket[item].push(item);
    }
    let pos = 0;
    for(let item of bigBucket){
        while(item != undefined && item.length != 0){
            arr[pos++] = item.shift()
        }
    }
    return arr;
}

时空复杂度及稳定性

时间复杂度仅为O(n)

空间复杂度O(n + k),k为额外用到的数组空间,但我们这里的实现直接原地覆盖掉原数组,个人认为空间复杂度仅为O(k),只额外用到了桶(数组)空间

稳定性:没有发生数据的交换,故稳定