实现思路与步骤
思路
- 设置固定空桶数
- 将数据放到对应的空桶中
- 将每个不为空的桶进行排序
- 拼接不为空的桶中的数据,得到结果
步骤演示
假设一组数据(20长度)为
[63,157,189,51,101,47,141,121,157,156,194,117,98,139,67,133,181,13,28,109]
现在需要按5个分桶,进行桶排序,实现步骤如下:
-
找到数组中的最大值194和最小值13,然后根据桶数为5,计算出每个桶中的数据范围为
(194-13+1)/5=36.4
- 遍历原始数据,(以第一个数据63为例)先找到该数据对应的桶序列
Math.floor(63 - 13) / 36.4) =1
,然后将该数据放入序列为1的桶中(从0开始算) - 当向同一个序列的桶中第二次插入数据时,判断桶中已存在的数字与新插入的数字的大小,按从左到右,从小打大的顺序插入。如第一个桶已经有了63,再插入51,67后,桶中的排序为(51,63,67) 一般通过链表来存放桶中数据,但js中可以使用数组来模拟
- 全部数据装桶完毕后,按序列,从小到大合并所有非空的桶(如0,1,2,3,4桶)
-
合并完之后就是已经排完序的数据
步骤图示
实现代码
JS实现代码(数组替代链表版本)
var bucketSort = function(arr, bucketCount) {
if (arr.length <= 1) {
return arr;
}
bucketCount = bucketCount || 10;
//初始化桶
var len = arr.length,
buckets = [],
result = [],
max = arr[0],
min = arr[0];
for (var i = 1; i < len; i++) {
min = min <= arr[i] ? min: arr[i];
max = max >= arr[i] ? max: arr[i];
}
//求出每一个桶的数值范围
var space = (max - min + 1) / bucketCount;
//将数值装入桶中
for (var i = 0; i < len; i++) {
//找到相应的桶序列
var index = Math.floor((arr[i] - min) / space);
//判断是否桶中已经有数值
if (buckets[index]) {
//数组从小到大排列
var bucket = buckets[index];
var k = bucket.length - 1;
while (k >= 0 && buckets[index][k] > arr[i]) {
buckets[index][k + 1] = buckets[index][k];
k--
}
buckets[index][k + 1] = arr[i];
} else {
//新增数值入桶,暂时用数组模拟链表
buckets[index] = [];
buckets[index].push(arr[i]);
}
}
//开始合并数组
var n = 0;
while (n < bucketCount) {
if (buckets[n]) {
result = result.concat(buckets[n]);
}
n++;
}
return result;
};
//开始排序
arr = bucketSort(arr, self.bucketCount);
JS实现代码(模拟链表实现版本)
var L = require('linklist'); //链表
var sort = function(arr, bucketCount) {
if(arr.length <= 1) {
return arr;
}
bucketCount = bucketCount || 10;
//初始化桶
var len = arr.length,
buckets = [],
result = [],
max = arr[0],
min = arr[0];
for(var i = 1; i < len; i++) {
min = min <= arr[i] ? min : arr[i];
max = max >= arr[i] ? max : arr[i];
}
//求出每一个桶的数值范围
var space = (max - min + 1) / bucketCount;
//将数值装入桶中
for(var i = 0; i < len; i++) {
//找到相应的桶序列
var index = Math.floor((arr[i] - min) / space);
//判断是否桶中已经有数值
if(buckets[index]) {
//数组从小到大排列
var bucket = buckets[index];
var insert = false; //插入标石
L.reTraversal(bucket, function(item, done) {
if(arr[i] <= item.v) { //小于,左边插入
L.append(item, _val(arr[i]));
insert = true;
done(); //退出遍历
}
});
if(!insert) { //大于,右边插入
L.append(bucket, _val(arr[i]));
}
} else {
var bucket = L.init();
L.append(bucket, _val(arr[i]));
buckets[index] = bucket; //链表实现
}
}
//开始合并数组
for(var i = 0, j = 0; i < bucketCount; i++) {
L.reTraversal(buckets[i], function(item) {
// console.log(i+":"+item.v);
result[j++] = item.v;
});
}
return result;
};
//链表存储对象
function _val(v) {
return {
v: v
}
}
//开始排序
arr = bucketSort(arr, self.bucketCount);
其中,linklist为引用的第三方库,地址
linklist