[路飞]-桶排序-bucket-sort

269 阅读3分钟

桶排序的依据

数据集里所有的数都在一个总区间内。找出这个总区间,然后在这个总区间里,人为划分子区间,把每个元素扔到属于他的子区间,然后我们只需要在每个子区间内做排序,整个数据集就能达到有序。

换句话说,如果数据集里所有的数不在一个总区间内。数据集没有一个上下界,就无法使用桶排序。

1. 准备空桶

什么是桶?

我们把处于同一区间的数据放在一块儿,这就是一个桶,一个桶内有处在同一区间的所有数据,然后在桶内做排序。子区间 = 桶。

子区间的长度是人为划分的,且长度统一。那么,做一次桶排序要用几个桶?即划分几个子区间?

总区间长度 = 子区间的个数 * 子区间的长度。

总区间长度,就是找出总数据集中最大的元素和最小的元素,最小的元素、最大的元素之间的距离就是总区间长度。

子区间涵盖了总区间里的一段区间,子区间越长,需要的子区间个数就越少,到时候放到单个子区间中的数据个数就越多。

比如假设有数据集[1,2,4,6,10],则其中最小值、最大值分别为1、10:

当人为设置子区间尺寸为2,就需要5个子区间<> <> <> <> <>,分别涵盖<1到2>、<3到4>、<5到6>、<7到8>、<9到10>区间的数据。

如果子区间尺寸为3,就需要4个区间<><><><>,分别涵盖<1到3><4到6><7到9><10>。

如果子区间尺寸为4,就需要3个区间<><><>,分别涵盖<1到4><5到8><9到10>。

因此,根据区间的尺寸,用到的区间数量是动态的。根据桶的大小,用到的桶的数量是动态的。

现在让我们用代码实现:根据数据集内最小最大值、桶的大小,动态地生成空桶。

function getBuckets(min,max,subRange){//传入人为设置的子区间长度subRange

    const range = max - min + 1 // 总区间长度

    const amount = Math.ceil(range/subRange)// 总长度除以子区间长度

    return new Array(amount).fill().map(()=>[])

}

console.log(getBuckets(1,10,2))// [ [], [], [], [], [] ]

console.log(getBuckets(1,10,3))// [ [], [], [], [] ]

console.log(getBuckets(1,10,4))// [ [], [], [] ]

2.把数据放入他所属的空桶(把数据放入它所属的区间)

还是以数据集[1,2,4,6,10]为例,比如把数据4放入桶:

当子区间尺寸为3时,放入<4到6>,是第二个桶

当子区间尺寸为4时,放入<1到4>,是第一个桶

让我们用代码实现:将数值放入桶。根据当前数值距数据集最小值的距离、子区间的大小,实现放入。

function insert(number,min,subRange,buckets){
    const bucketIndex = Math.floor((number - min) / subRange)

    buckets[bucketIndex].push(number)
    
    return buckets

}
// 
console.log(insert(4,1,3,getBuckets(1,10,3)))// [ [], [ 4 ], [], [] ] 若人为设置subRange为3,数据4是放入第二个桶
console.log(insert(4,1,4,getBuckets(1,10,4)))// [ [ 4 ], [], [] ]     若人为设置subRange为4,数据4是放入第一个桶


3.对桶内进行排序


function sortInBucket(bucket){

   insertionSort(bucket)// 仅仅使用插入排序进行桶内排序

   return bucket

}

console.log(sortInBucket([1,5,2,3]))// [1,2,3,5]

4.最后:把有序的数据从每个桶里拿出来

function sortedArrayFromBuckets(buckets){

    return buckets.flat()


}

console.log(sortedArrayFromBuckets([[1,2,3,5],[6,7,8]]))// [1,2,3,5,6,7,8]

完整代码😄


function bucketSort(arr,subRange = 3){

    if (!arr.length) return arr
    

    // 获取min和max

    let min = arr[0]

    let max = arr[0]

    for (let i = 1; i < arr.length; i++) {

        if (arr[i] < min) min = arr[i]

        if (arr[i] > max) max = arr[i]

    }

    // 1.生成空桶

    const buckets = getBuckets(min,max,subRange)


    // 2.把数据放入桶

    for (let i = 0; i < arr.length; i++) {

        insert(arr[i],min,subRange,buckets)

    }


    // 3.桶内排序

    for (let i = 0; i < buckets.length; i++) {

        const bucket = buckets[i]

        sortInBucket(bucket)

    }



    // 4.把有序的数据从桶里拿出来

    return sortedArrayFromBuckets(buckets)

}

console.log(bucketSort([5,4,6,7,3,2,8,1111]))


function insertionSort(input,n = 1){

    for(let i = n;i<input.length;i += n){

        let cur = input[i]

        let j = i - n

        while(j>=0 && input[j]>cur){

            input[j+n] = input[j]

            j -=n

        }

        input[j+n] = cur

    }

}
function getBuckets(min,max,subRange){//传入人为设置的子区间长度subRange

    const range = max - min + 1 // 总区间长度

    const amount = Math.ceil(range/subRange)// 总长度除以子区间长度

    return new Array(amount).fill().map(()=>[])

}

function insert(number,min,subRange,buckets){
    const bucketIndex = Math.floor((number - min) / subRange)

    buckets[bucketIndex].push(number)
    
    return buckets

}

 function sortInBucket(bucket){

    insertionSort(bucket)// 仅仅使用插入排序进行桶内排序

    return bucket

}

function sortedArrayFromBuckets(buckets){

    return buckets.flat()


}