前言
在之前的计数排序中,排序成员只能为整数型,不能对浮点型的成员进行排序。对此,桶排序(Bucket sort)是弥补了这一缺点的排序算法。
实现原理
对于桶排序而言,其核心思想是分治思想(化大为小)。
首先,根据数据成员的值和个数进行范围划分(确定桶的范围,桶的数量):
每个桶的范围长度:
R
=
V
m
a
x
−
V
m
i
n
N
e
l
e
m
+
1
R = \frac{V_{max}-V_{min}}{N_{elem}}+1
R=NelemVmax−Vmin+1
桶的个数:
N
=
V
m
a
x
−
V
m
i
n
R
+
1
N = \frac{V_{max}-V_{min}}{R}+1
N=RVmax−Vmin+1
根据上述的公式,可以确定每个桶的范围分别为:
[
b
1
,
b
2
,
b
3
,
.
.
.
,
b
N
B
u
c
k
e
t
]
⇒
[
[
V
m
i
n
,
V
m
i
n
+
R
)
,
[
V
m
i
n
+
R
,
V
m
i
n
+
2
∗
R
)
,
.
.
.
,
[
V
m
i
n
+
(
N
−
1
)
∗
R
,
V
m
i
n
+
N
∗
R
)
]
[b_1,b_2,b_3,...,b_{N_{Bucket}}]\Rightarrow [[V_{min},V_{min}+R),[V_{min}+R, V_{min}+2*R),...,[V_{min}+(N-1)*R,V_{min}+N*R)]
[b1,b2,b3,...,bNBucket]⇒[[Vmin,Vmin+R),[Vmin+R,Vmin+2∗R),...,[Vmin+(N−1)∗R,Vmin+N∗R)]
其次,根据该关系,扫描待排序成员,将其放在符合范围内的桶中:
例如,{5.1,2.3,3.9,6.6,9.6,10.5},R和N分别为:R = (10.5-2.3)/6 +1 = 2 和 N = 8.2/2 + 1 = 5
之后,对每个桶的元素进行排序。
最后,依次输出每个桶的元素,构成最终的排序结果。
实现代码:
头文件:
class BucketSort {
public:
BucketSort(std::vector<double>& ele);
~BucketSort() {};
void Sort();
double operator[](int i)const;
private:
std::vector<double> elements;
};
根据上述原理,实现的桶排序:
BucketSort::BucketSort(std::vector<double>& ele):elements(ele) {};
void BucketSort::Sort() {
//求出最小值与最大值
auto minmax_v = std::minmax_element(elements.begin(), elements.end());
double min = *minmax_v.first, max = *minmax_v.second;
double range = (max - min) / elements.size()+1;
int nums = static_cast<int>((max - min) / range + 1);
//初始化桶容器
std::vector<std::vector<double>> bucket(nums);
//桶容器分配元素
for (auto item : elements) {
for (int i = 0; i < nums; i++) {
if (item < (min + (i + 1) * range) && item >= (min + i * range))
bucket[i].push_back(item);
}
}
//针对桶的内容进行排序
for (int i = 0; i < nums; i++) std::sort(bucket[i].begin(), bucket[i].end());
//进行排序
int count = 0;
for (int i = 0; i < nums; i++) {
for (auto item : bucket[i]) {
elements[count++] = item;
}
}
}
double BucketSort::operator[](int i) const{
return elements[i];
}
结果:
int main()
{
std::vector<double> elem= { 0.8, 9.2, 3.4, 8.4, 5.6, 1.1, 6.5, 6.4 };
BucketSort bs(elem);
bs.Sort();
for (int i = 0; i < elem.size(); i++) std::cout << bs[i] << ", ";
return 0;
}
上述代码仍存在部分问题:①分配元素的过程为O(N^2),可优化为O(N),关键在元素分配给对应桶的计算过程;②分配的变量过多,可以进一步删减等。后续会进行优化和更新。
程序复杂度
在时间复杂度上,桶排序的平均时间复杂度为O(n),最坏时间复杂度为O(nlogn),空间复杂度为O(N),桶排序在对等值元素进行排序是稳定的。
注意事项
在待排序元素的值分布为不均匀时,尤其是极端不均匀情况下,会出现第一个桶有n-1个元素,而最后的桶只有一个元素的问题,白白浪费空间。