一、 桶排序
桶排序(Bucket sort)是一种基于计数的排序算法的推广(计数排序,顾名思义就是要统计出现的次数,适用于对数值范围很小的正数序列排序。在统计每个数的出现次数后,我们可以直接构造出排序结果),工作的原理是将数据分到有限数量的桶子里,然后每个桶再分别排序(有可能再使用别的排序算法或是以递归方式继续使用桶排序进行排序)
二、算法步骤
-
设置固定数量的空桶。(有的是设置每个可以装的数值范围,人为设置一个BucketSize,作为每个桶所能放置多少个不同数值(例如当BucketSize==5时,该桶可以存放{1,2,3,4,5}这几种数字,但是容量不限,即可以存放100个3),其实无论是设置桶的数量,还是数值范围,其实他们之间的关系就是floor((max-min)/桶的数量)=每个桶的数值范围)
-
把数据放到对应的桶中。
-
对每个不为空的桶中数据进行排序。
-
拼接不为空的桶中数据,得到结果。
三、算法演示
图片截取自网络
四、排序动画过程解释
-
首先,设置固定数量的空桶,在这里为了方便演示,设置桶的数量为 5 个空桶
-
遍历整个数列,找到最大值为 56 ,最小值为 2 ,每个桶的范围为 ( 56 - 2 + 1 )/ 5 = 11
-
再次遍历整个数列,按照公式 floor((数字 – 最小值) / 11) 将数字放到对应的桶中
-
比如,数字 7 代入公式 floor((7 – 2) / 11) = 0 放入 0 号桶
-
数字 12 代入公式 floor((12 – 2) / 11) = 0 放入 0 号桶
-
数字 56 代入公式 floor((56 – 2) / 11) = 4 放入 4 号桶
-
当向同一个索引的桶,第二次插入数据时,判断桶中已存在的数字与新插入数字的大小,按照左到右,从小到大的顺序插入(可以使用前面讲解的插入排序)实现
-
比如,插入数字 19 时, 1 号桶中已经有数字 23 ,在这里使用插入排序,让 19 排在 23 前面
-
遍历完整个数列后,合并非空的桶,按从左到右的顺序合并0,1,2,3,4桶。
-
这样就完成了 桶排序
五、js代码实现
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]; //输入数据的最大值
}
}
//桶的初始化
//设置桶内存放值得大小为5,
var DEFAULT_BUCKET_SIZE = 5;
bucketSize = bucketSize || DEFAULT_BUCKET_SIZE;
//桶的数量
var bucketCount = Math.floor((maxValue - minValue) / bucketSize) + 1;
//new一个数组长度为桶的数量的数组,说白了就是构造n个桶
var buckets = new Array(bucketCount);
//让每一个桶成为一个单独的有序数组,目的是用来装填需要排序的数组子元素
for (i = 0; i < buckets.length; i++) {
buckets[i] = [];
}
//利用映射函数将数据分配到各个桶中
for (i = 0; i < arr.length; i++) {
floor(当前值-最小值)/每个桶的取值范围=该数值对应的桶
buckets[Math.floor((arr[i] - minValue) / bucketSize)].push(arr[i]);
}
arr.length = 0;
for (i = 0; i < buckets.length; i++) {
insertionSort(buckets[i]); //对每个桶进行排序,这里使用了插入排序
for (var j = 0; j < buckets[i].length; j++) {
arr.push(buckets[i][j]);
}
}
return arr;
}
function insertionSort(iArr) {
var n = iArr.length;
// 从第一个元素开始,该元素可以认为已经被排序
for (var i = 1; i < n; i++) {
// 取出下一个元素,在已经排序的元素序列中从后向前扫描
for (var j = i - 1; j >= 0; j--) {
if (iArr[i] >= iArr[j]) {
// 如果该元素(已排序)小于新元素,将新元素插入该元素下一位置
iArr.splice(j + 1, 0, iArr.splice(i, 1)[0]);
break;
} else if (j === 0) {
// 如果已是最小元素,则插入最左侧
iArr.splice(j, 0, iArr.splice(i, 1)[0]);
}
}
}
return (iArr)
}