一、桶排序的原理
桶排序是计数排序的升级版。它利用了函数的映射关系,高效与否的关键就在于这个映射函数的确定。思路大致是,
- 设置一个定量的数组当作空桶;
- 遍历输入数据,并且把数据一个一个放到对应的桶里去;
- 对每个不是空的桶进行排序;
- 从不是空的桶里把排好序的数据拼接起来。
二、静态示意图
元素分布在桶中:
然后,元素在每个桶中排序:
其实,很明显,这是计数排序的优化版,当然也可以说成是升级版。主要是分好桶。
三、代码实现
function bucketSort(arr, bucketSize) {
if (arr.length === 0) {
return arr;
}
var i;
var minValue = arr[0];
var maxValue = arr[0];
//求出最大值和最小值,这里有两种方式,两种方式只用其一,这里采用方式二。方式一:
//for (i = 1; i < arr.length; i++) {
// if (arr[i] < minValue) {
// minValue = arr[i]; // 输入数据的最小值
// } else if (arr[i] > maxValue) {
// maxValue = arr[i]; // 输入数据的最大值
// }
// }
//方式二:
maxValue = Math.max(...arr);
minValue = Math.min(...arr);
//桶的初始化
var DEFAULT_BUCKET_SIZE = 5; // 设置桶的默认数量为5
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
var buckets = new Array(bucketCount);
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
//利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); // 对每个桶进行排序,这里使用了插入排序,这里也可以直接用js的数组sort方法,很直接
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
就这样,整个代码已经写完。其实,这里比较难的应该是映射到不同桶的方法。
我们可以一步步分解。首先,我们需要分桶,我们一开始传入了桶的数量,但是这个桶的数量bucketSize也是一个过程变量,它需要用来计算真正的桶的数量。这个真正的变量是bucketCount 变量。那么bucketSize变量计算用,包括了确定桶真正数量的时候,以及数据依次映射到不同桶的时候,都要用它来做参考。那么,如果我们不传入一个固定的bucketSize,我们也可以拿数组的长度或者数组长度的一半之类的固定参数作为默认桶的数量。
四、图解过程
我们了解了以上的原理,然后我们代码写过一次。那么,我们现在再巩固一下。我们现在有以下的数组:
现在我们要确定桶的数量,怎么确定?这里面有一个计算公式,
桶的数量 = (最大值 - 最小值)/ 数组长度 + 1。 数组长度 可以 是 任意自定义的有意义的正整数。
那这里面我们可以确定桶的数量为 49 / 13 +1 = 4;
现在我们再用另一个公式确定每个元素在哪个桶中
元素位置 =( 元素大小 - 最小值)/ 数组长度
根据这个公式我们可以确定每个元素都在第几个桶里面
现在我们再对每一个桶进行排序(这里面没有固定的排序算法)
对每个桶进行排序后最后我们进行整合,就变成了一个有序的序列。
五、复杂度
桶排序须要两个辅助空间:
(1)第一个辅助空间,是桶空间B;
(2)第二个辅助空间,是桶内的元素链表空间;
总的来说,空间复杂度是O(n)。
桶 X 内的所有元素,是始终有序的;
插入排序是稳固的,因而桶内元素程序也是稳固的;
参考文章:
关于javascript:排序算法桶排序
JS实现桶排序(代码+讲解)
JavaScript 数据结构与算法之美 - 桶排序、计数排序、基数排序