-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathu1-bucket-sort.md
76 lines (53 loc) · 2.62 KB
/
u1-bucket-sort.md
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
# 桶排序
## 过程分析
桶排序同样是一种线性时间的排序算法。类似于计数排序所创建的统计数组,桶排序需要创建若干个桶来协助排序。

桶排序的基本思想是:
```
把数组 arr 划分为 n 个大小相同子区间(桶),每个子区间各自排序,最后合并
```
**计数排序是桶排序的一种特殊情况**,可以把计数排序当成每个桶里只有一个元素的情况。
1. 找出待排序数组中的最大值 max, 最小值 min
2. 用数组作为桶,桶里放的元素也是数组存储,桶的数量 (max-min) / arr.count + 1
3. 变量数组 arr, 计算每个元素 arr[i] 放的桶
4. 每个桶各自排序
5. 遍历桶数组,把排序好的元素放进输出数组
## 代码实现
```swift
var tmp = [4, 32, 5, 6, 8, 10, 31, 7]
/// 桶排序
func bucketSort(_ arr: [Int]) -> [Int] {
guard let max = arr.max(),
let min = arr.min()
else { return arr }
/// 桶数目
let bucketNum = (max - min) / arr.count + 1
var bucketArr:[[Int]] = (0..<bucketNum).map { _ in return [Int]() }
/// 将每个元素放入桶
for ele in arr {
let num = (ele - min) / arr.count
bucketArr[num].append(ele)
}
/// 对每个桶进行排序
let tmpArr:[[Int]] = bucketArr.compactMap { subArr in
return subArr.sorted(by: <)
}
/// 组装
var retArr = [Int]()
for item in tmpArr {
for sItem in item {
retArr.append(sItem)
}
}
return retArr
}
let ret = bucketSort(tmp)
print("ret = \(ret)")
```
## 性能
时间消耗包括两部分一部分为初始化桶,连接排好序的桶,其时间复杂度为 O(n) 一般有 m < n (m 个桶)
另一部分为对桶中的元素进行排序,这部分的复杂度,通过代码中的 for 和 while 循环直接看不太容易,这样考虑:每个桶里面有 ni 个元素,对 ni 个元素进行插入排序的耗时为 O(ni^2)。
于是 `T(n)=O(n)+∑O(ni^2)`,平均意义下认为 ni=n/m,于是有 `T(n)=O(n)+m*O((n/m)^2)=O(n^2/m)+O(n)`
当 n=m 时,`T(n)=O(n)+O(n)=O(n)`
对于每个桶采用其他的排序算法:m 个桶,每个桶中的元素平均假设有 n/m 个,在上面进行基于比较的排序,复杂度不会低于 `n * O(n/m * lg(n/m))`,平均意义下每个桶中的元素有 n/m 个,`O(m * n/m * lg(n/m) = O(n*lg(n/m))`,所以总的时间复杂度为 `T(n)=O(n+n*lg(n/m))`
当 m=n 时时间复杂度为 O(n),此时和计数排序一样,桶数量越多,时间效率越高,然而桶数量越多占用空间也就越大。