桶排序
桶排序
桶排序(Bucket Sort)是一种分布式排序算法,核心思想是将待排序数据分散到若干个“桶”中,对每个桶内的数据单独排序(可复用插入排序、快速排序等),最后将所有桶的元素按顺序合并,得到整体有序的结果。它的效率高度依赖数据的分布特性,适合处理分布均匀的数值型数据(如浮点数、整数)。
一、桶排序的核心原理
- 划分桶:根据数据的范围和分布,创建若干个空桶(通常是列表的列表),每个桶负责一个特定的数值区间。
- 分配元素:遍历待排序数据,将每个元素放入对应的桶中(根据元素值落在哪个区间判断)。
- 桶内排序:对每个非空桶内的元素单独排序(可使用插入排序、快速排序等,小规模数据常用插入排序)。
- 合并结果:按桶的顺序依次取出所有元素,拼接成最终的有序数组。
二、关键步骤详解
以排序数组 [3.2, 0.4, 1.5, 2.8, 3.6, 0.1, 2.1]
为例,步骤如下:
1. 确定桶的数量和区间
- 先找到数组的最小值
min=0.1
和最大值max=3.6
。 - 假设设置桶的数量为
5
(通常可设为与数据量相同,或根据分布调整)。 - 计算每个桶的区间大小:
bucket_size = (max - min) / 桶数量 = (3.6 - 0.1)/5 = 0.7
。 - 5个桶的区间分别为:
桶0:[0.1, 0.8)
(0.1 + 0×0.7 ~ 0.1 + 1×0.7)
桶1:[0.8, 1.5)
桶2:[1.5, 2.2)
桶3:[2.2, 2.9)
桶4:[2.9, 3.6]
(最后一个桶需包含最大值)
2. 分配元素到桶
3.2
落在桶4(2.9~3.6)→ 桶4:[3.2]
0.4
落在桶0(0.1~0.8)→ 桶0:[0.4]
1.5
落在桶2(1.5~2.2)→ 桶2:[1.5]
2.8
落在桶3(2.2~2.9)→ 桶3:[2.8]
3.6
落在桶4 → 桶4:[3.2, 3.6]
0.1
落在桶0 → 桶0:[0.4, 0.1]
2.1
落在桶2 → 桶2:[1.5, 2.1]
3. 桶内排序
对每个桶单独排序:
- 桶0:
[0.1, 0.4]
(排序后) - 桶1:
[]
(空桶,跳过) - 桶2:
[1.5, 2.1]
(排序后) - 桶3:
[2.8]
(无需排序) - 桶4:
[3.2, 3.6]
(排序后)
4. 合并桶
按桶0→桶1→桶2→桶3→桶4的顺序拼接元素,得到最终结果:
[0.1, 0.4, 1.5, 2.1, 2.8, 3.2, 3.6]
三、时间复杂度与空间复杂度
时间复杂度:
桶排序的完整流程可分为4步,每一步的时间消耗如下:
- 创建桶:初始化k个空桶,耗时O(k)(可忽略,因k通常远小于n)。
- 分配数据:将n个数据分到k个桶中,每个元素需计算所属桶的索引(如通过映射函数),耗时O(n)(线性遍历)。
- 桶内排序:对每个非空桶内的元素单独排序,耗时取决于桶内元素数量和排序算法。
- 合并结果:按桶的顺序依次输出所有桶内元素,总元素数为n,耗时O(n + k)(遍历k个桶,输出n个元素)。
最好情况:数据均匀分布时的O(n + k)
场景:数据在k个桶中“均匀分布”,即每个桶内的元素数量大致相等(设为m = n/k)。
-
桶内排序的总耗时分析
假设桶内使用插入排序(适合小批量数据,虽然时间复杂度是O(m²),但常数项极小):- 单个桶的排序耗时:O(m²) = O((n/k)²)(因m = n/k)。
- k个桶的总排序耗时:k × O((n/k)²) = O(n²/k)(k个桶的开销相加)。
-
总时间复杂度计算
- 总耗时 = 分配数据(O(n)) + 桶内排序(O(n²/k)) + 合并结果(O(n + k))。
- 忽略低阶项后,总复杂度为 O(n²/k + n + k)。
-
当k≈n时,为何接近O(n)?
若桶的数量k与数据量n接近(如k = n):- 每个桶内的元素数量m = n/k ≈ 1(几乎每个桶只有1个元素)。
- 此时桶内排序耗时:k × O(1²) = O(k) ≈ O(n)(单个元素无需排序,耗时为常数)。
- 总耗时简化为:O(n)(分配) + O(n)(排序) + O(n)(合并) = O(n)。
最坏情况:数据极端集中时的退化
场景:所有数据被分配到同一个桶中(如数据全部相等,或映射函数设计不合理)。
- 各步骤耗时变化
- 分配数据:仍需遍历n个元素,耗时O(n)。
- 合并结果:仅需输出1个桶的n个元素,耗时O(n)。
- 关键瓶颈:桶内排序。此时相当于对n个元素用桶内选择的排序算法排序,总耗时完全由该算法决定。
- 不同桶内排序算法的影响
- 若用插入排序:单个桶内n个元素的排序耗时为O(n²),总复杂度为O(n²)。
- 若用快速排序:平均耗时O(n log n),但最坏情况(如数据有序)仍为O(n²)。
- 若用堆排序/归并排序:稳定耗时O(n log n),总复杂度为O(n log n)。
空间复杂度:
O(n + k)
,需要存储 n
个元素和 k
个桶。
四、代码
下面的代码支持整数和浮点数排序,桶内使用 Python 内置的 sorted
函数(基于 Timsort,效率较高):
def bucket_sort(arr):if len(arr) < 2:return arr # 空数组或单元素数组直接返回# 步骤1:确定数据范围min_val = min(arr)max_val = max(arr)# 处理所有元素相同的情况(避免桶大小为0)if min_val == max_val:return arr# 步骤2:创建桶(数量设为与数据量相同,可根据实际分布调整)bucket_count = len(arr)bucket_size = (max_val - min_val) / bucket_count # 每个桶的区间大小buckets = [[] for _ in range(bucket_count)] # 初始化桶(列表的列表)# 步骤3:将元素分配到对应桶中for num in arr:# 计算元素应放入的桶索引(避免索引越界,最后一个桶单独处理)index = int((num - min_val) / bucket_size)# 若索引等于桶数量(可能因浮点数精度问题),放入最后一个桶if index == bucket_count:index -= 1buckets[index].append(num)# 步骤4:对每个桶排序,并合并结果sorted_arr = []for bucket in buckets:# 桶内排序(使用内置sorted,效率高)sorted_bucket = sorted(bucket)# 合并到结果数组sorted_arr.extend(sorted_bucket)return sorted_arrif __name__ == "__main__":# 测试浮点数float_arr = [3.2, 0.4, 1.5, 2.8, 3.6, 0.1, 2.1]print("浮点数排序前:", float_arr)print("浮点数排序后:", bucket_sort(float_arr)) # [0.1, 0.4, 1.5, 2.1, 2.8, 3.2, 3.6]# 测试整数int_arr = [5, 2, 9, 1, 5, 6]print("\n整数排序前:", int_arr)print("整数排序后:", bucket_sort(int_arr)) # [1, 2, 5, 5, 6, 9]