桶排序
一、原理
☆思想:对输入数据有要求,要在一定区间内,如课程成绩在[0,100]等;如果元素值差别太大,会造成桶内元素数量不均,桶排序就失去了意义。
桶排序是计数排序的变种,把计数排序中相邻的多个”小桶”放到一个”大桶”中,分完桶后,再对每个桶进行排序(一般用快排),最后合并所有桶得到有序数列;
通常情况下,桶的大小有两种取法,第一种是取一个10^n或2^n的数,方便实现,另一种是取数列的最大值和最小值然后均分作桶;
为了使桶排序更加高效,我们需要做到这两点:
1、在额外空间充足的情况下,尽量增大桶的数量
2、使用的映射函数能够将输入的 N 个数据均匀的分配到 K 个桶中
补充:桶个数一般取 k,k^2 = n,n是所有元素个数
☆过程:以递增为例,输入序列用数组arr表示,长度为n;获取输入数组arr最大最小值,计算桶的数量和大小,申请辅助二维数组brr表示桶,一行表示一个桶,同一行不同列表示同一个桶内的不同元素;遍历arr,把元素值放入桶内,使用其它排序算法分别对每个桶内元素排序,完成后再把所有桶合并,得到有序数组;
二、实现代码
JavaScript 代码实现方法一,入桶时插入排序,再合并各个桶:
function bucketsSort(arr) { var brr = []; var index, buckets, baseVal; var arrMaxVal, arrMinVal; var sortedIndex = 0, n = arr.length; //获取最大、最小元素 arrMaxVal = arr[0]; arrMinVal = arr[0]; for (var i = 0; i < n; i++) { if (arrMaxVal < arr[i]) { arrMaxVal = arr[i]; } if (arrMinVal > arr[i]) { arrMinVal = arr[i]; } } //最大最小值相同时说明输入序列完全一致,结束排序 if (arrMaxVal == arrMinVal) return; //桶数 buckets = Math.ceil(Math.sqrt(n)); //桶大小 baseVal = Math.ceil((arrMaxVal - arrMinVal + 1) / buckets); //按桶大小放入桶内 for (var i = 0; i < n; i++) { //桶索引 index = Math.floor((arr[i] - arrMinVal) / baseVal); //桶元素为空,把元素转为数组插入 if (!brr[index]) { brr[index] = [arr[i]]; } //桶元素是一个数组 else if (Array.isArray(brr[index])) { //找到插入数组位置,保证插入后数组有序 for (var j = brr[index].length - 1; j >= 0 && brr[index][j] > arr[i]; j--) { //元素后移一位 brr[index][j + 1] = brr[index][j]; } //新进元素就位 brr[index][j + 1] = arr[i]; } } //把排序结果复制到输入数组 for (var i = 0; i < brr.length; i++) { if (Array.isArray(brr[i])) { for (var j = 0; j < brr[i].length; j++) { arr[sortedIndex++] = brr[i][j]; } } } }
JavaScript 代码实现方法二,先分桶,对每个桶单独排序,采用递归,再合并各个桶:
function bucketsSort(arr) { var brr = []; var index, buckets, baseVal; var arrMaxVal, arrMinVal; var sortedIndex = 0, n = arr.length; //获取最大、最小元素 arrMaxVal = arr[0]; arrMinVal = arr[0]; for (var i = 0; i < n; i++) { if (arrMaxVal < arr[i]) { arrMaxVal = arr[i]; } if (arrMinVal > arr[i]) { arrMinVal = arr[i]; } } //最大最小值相同时说明输入序列完全一致,结束排序 if (arrMaxVal == arrMinVal) return; //桶数 buckets = Math.ceil(Math.sqrt(n)); //桶大小 baseVal = Math.ceil((arrMaxVal - arrMinVal + 1) / buckets); //按桶大小放入桶内 for (var i = 0; i < n; i++) { //桶索引 index = Math.floor((arr[i] - arrMinVal) / baseVal); //桶元素为空,把元素转为数组插入 if (!brr[index]) { brr[index] = [arr[i]]; } else { brr[index].push(arr[i]); } } //对桶内元素递归排序 for (var i = 0; i < brr.length; i++) { if (brr[i]) { if (brr[i].length > 1) { bucketsSort(brr[i]); } } } //把排序结果复制到输入数组 for (var i = 0; i < brr.length; i++) { if (Array.isArray(brr[i])) { for (var j = 0; j < brr[i].length; j++) { arr[sortedIndex++] = brr[i][j]; } } } }
三、优化
无;
四、复杂度
| 名称 | 时间复杂度 | 空间复杂度 | 稳定性 | ||
| 平均 | 最坏 | 最优 | |||
| 桶排序 | O(n) | O(n+m) | |||
对于待排序序列大小为 n,共分为 m 个桶,主要步骤有:
- n 次循环,求序列最大最小值
- n 次循环,将每个元素装入对应的桶中
- m 次循环,对每个桶中的数据进行排序(平均每个桶有 n/m 个元素),一般使用较为快速的排序算法,时间复杂度为 O(nlogn)
- n 次循环,输出排序数列
加起来,总的运算量为 3n+ m* n/m * log(n/m ) = 3n+n(logn-logm),去掉系数,整个桶排序的时间复杂度为:O( n∗ ( log(n/m) +1 ) ),当 n = m 时,复杂度为 O(n);
本排序需要申请m额外空间,m为区间长度;
本排序稳定性取决于桶内排序使用的算法。
参考资料:

浙公网安备 33010602011771号