排序算法之桶排序
这里是传送门⇒总结:关于排序算法
平均时间复杂度 | 最优时间复杂度 | 最差时间复杂度 | 空间复杂度 | 稳定性 | |
---|---|---|---|---|---|
桶排序 | O(n + n(logn - logk)) | O(n) | O(n2) | O(n+k) | 稳定 |
注:k为桶数量
桶排序中的“桶”代表一个取值区间,里面可以放若干个元素
桶排序是计数排序的改进版本:计数排序可以看成每个“桶”只放相同元素,而桶排序每个“桶”放的是一定范围的元素。计数排序申请的空间跨度从最小元素值到最大元素值,若待排序集合中元素不是依次递增的,则必然有空间浪费情况;桶排序则是弱化了这种浪费情况,尽量减少了元素值大小不连续情况下的空间浪费情况
桶排序需要尽量保证元素分散均匀,否则当所有数据集中在同一个桶中时,桶排序失效
- 算法描述
- 首先应该创建“桶”,即确定若干个取值区间,而如何确定这些区间的方式有多种
- 其中一种划分区间的方式:创建的“桶”数量为待排序列长度,最后一个“桶”只包含待排序列的最大值,其他“桶”的区间范围跨度为“最大值与最小值之差均分”。比如,待排序列为
[4.5,0,84,3.25,2.18,0.5]
,那么“桶”数量为5,区间跨度为(4.5-0.5)/(5-1) = 1
,也就是各个“桶”的取值范围为[0.5,1.5)、[1.5,2.5)、[2.5,3.5)、[3.5,4.5)、[4.5,4.5]
- 然后遍历待排序列,把元素按区间取值分到对于的“桶”里
- 而对于分在(包含1个以上元素的)桶里的元素可以用其他排序算法(比如适合一定取值范围的计数排序)或者以递归方式继续使用桶排序进行排序
- JS实现
// 此处传入的array会被直接改变
function BucketSort(array) {
var len = array.length;
if (len <= 1) {
return;
}
var max = min = array[0];
for (var i = 0; i < len; i++) {
if (array[i] > max) {
max = array[i];
}
if (array[i] < min) {
min = array[i];
}
}
var bucketLen = len;
var gap = Math.floor((max - min) / bucketLen) + 1;
var bucket = new Array(bucketLen);
for (var i = 0; i < bucketLen; i++) {
bucket[i] = [];
}
for (var i = 0; i < len; i++) {
var index = Math.floor((array[i] - min) / gap);
bucket[index].push(array[i]);
}
array.length = 0;
for (var i = 0; i < bucketLen; i++) {
var sBucketLen = bucket[i].length;
if (sBucketLen > 1) {
QuickSort(bucket[i], 0, sBucketLen - 1);
for (var j = 0; j < sBucketLen; j++) {
array.push(bucket[i][j]);
}
}
}
}
- 分析
- 桶排序的复杂度分两部分:分配元素到对应“桶”的复杂度和进行“桶”内排序时使用的排序算法复杂度
- 前者的时间复杂度为O(n),后者则由使用的排序算法决定
- 若后者使用的排序算法是基于键值比较的算法(比如快排),基于键值比较的算法的时间复杂度下限为O(nlogn),设待排序列长度为n,前者中创建了m个“桶”,且分配均匀,即每个“桶”内的元素有k =
$\frac{n}{m}$
,那么后者的平均时间复杂度T(n) =$m \times O(k\log k)$
=$m \times O(\frac{n}{m}\log \frac{n}{m}$
) = O($n\log \frac{n}{m}$
),当“桶”数量接近待排序列元素数量n时,$\log \frac{n}{m}$
就会是一个较小的常数,这时时间复杂度接近O(n),所以桶排序的最优时间复杂度T(n) = O(n) - 若“桶”内排序使用的是其他排序,而且后者使用的是原地排序,即后者的空间复杂度为O(1)时,桶排序的空间复杂度仅由前者的空间复杂度决定,而前者主要是看bucket使用的空间,即“桶”数量 + 桶内使用的空间 = m + n,则桶数量的时间复杂度为O(n + m)
- 很显然,“桶”划分的区间越小,各个“桶”内的数据越少,“桶”内排序所用的时间也会越少。但相应的空间消耗就会增大,因为要多创建“桶”
- 上文表格桶排序的复杂度数据为对“桶”内元素使用直接插入排序的桶排序的复杂度(除了平均复杂度。平均复杂度是用基于键值比较的排序算法整体来算的)