桶排序(bucket sort)之基数排序
简单理解
桶排序是通过使用不同的桶容器来容纳序列中的数,然后再按桶的顺序进行收集,以实现有序的序列。而基数排序则是使用十个桶容器将一个待排序的序列排序。其中十个桶的大小是可以根据需要进行改变的,分别代表0-9十个数字,用来容纳每一趟排序中的相应位置上的数。
核心算法思想描述
以序列97、3、100、77从小到大排序为例。
1、对序列中的每个值,根据值的个位的数字,将其放到相应的桶中。例如,97放在数字'7'这个桶中,3放在数字'3'这个桶中,100放在数字'0'这个桶中,77放在数字'7'这个桶中。这个过程被称为“分布过程”。
分布后各桶的数据如图:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
100 | null | 3 | null | null | null | null | 97 | null | null |
null | null | null | null | null | null | null | 77 | null | null |
2、对各个桶进行遍历,并把其中的数字按照从桶'0'到桶'9'的顺序依次取出收集到序列中。这个过程被称为“收集过程”。 | |||||||||
收集后序列如下:100、3、97、77,可见个位数已经有序。 |
3、随后对十位和百位依次重复这两个过程,过程示意如下:
对十位分布:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
100 | null | null | null | null | null | 77 | null | null | 97 |
3 | null | null | null | null | null | null | null | null | null |
对十位收集:100、3、77、97。 |
对百位分布:
0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
---|---|---|---|---|---|---|---|---|---|
3 | 100 | null | null | null | null | null | null | null | null |
77 | null | null | null | null | null | null | null | null | null |
97 | null | null | null | null | null | null | null | null | null |
对百位收集:3、77、97、100。 |
算法性能分析
- 空间效率:一趟排序需要的辅助存储空间为r(r个容器),但以后的排序中重复使用这些容器,所以基数排序的空间复杂度为\(O(r)\)。
- 时间效率:基数排序需要进行d趟分配和收集,一趟分配需要\(O(n)\),一趟收集需要\(O(r)\),所以基数排序的时间复杂度为\(O(d(n + r))\),它与序列的初始状态无关。
- 稳定性:基数排序能够保持相对顺序,是稳定的。
C++实现
考虑到桶排序中各个桶的大小是不确定的,在每一趟排序的过程中大小可能产生变化,故我们使用vector实现各个桶,并用array将十个由vector实现的桶整合在一起。
#include <iostream>
#include <vector>
#include <array>
using namespace std;
void bucketSort(int a[], int n) {
array<vector<int>, 10> buckets;
// 确定最大数的位数,最多的位数决定需要进行多少趟桶排序
int max = a[0];
for (int i = 0; i < n; ++i)
if (max < a[i])
max = a[i];
int times = 0;
while (max) {
++times;
max /= 10;
}
// 进行桶排序
int div = 10;
for (int time = 0; time < times; ++time) {
// 数字分散于桶中
for (int i = 0; i < n; ++i)
buckets[a[i] % div / (div / 10)].push_back(a[i]); // 取相应位置上的数字
// 收集桶中的数据并清空桶
int cnt = 0;
for (int i = 0; i < 10; ++i) {
for (size_t j = 0; j < buckets[i].size(); ++j)
a[cnt++] = buckets[i][j];
buckets[i].clear();
}
div *= 10;
}
}
int main() {
const int SIZE = 20;
int a[SIZE] = { 0 };
for (int i = 0; i < SIZE; ++i) {
a[i] = rand() % 1000;
cout << a[i] << " ";
}
cout << "\n\n";
bucketSort(a, SIZE);
for (auto elem : a)
cout << elem << " ";
return 0;
}