-计数排序也称桶排序,可以在 $O(n)$ 的时间复杂度内对数组进行排序,但是它的空间复杂度与需要排序的数组的值域相关。
+桶排序适用于待排序数据值域较大但分布比较均匀的情况,是一个期望时间复杂度为 $O(n)$ 的排序算法。
-但实际操作时,由于时空同阶,时间复杂度应为 $O(\max\left(n,U\right))$ ,其中 $U$ 代表数组元素的值域大小。
+其大致思想是对值域进行分块,每块分别排序。由于每块元素不多,一般使用插入排序。如果使用稳定的内层排序,并且将元素插入桶中时不改变相对顺序,那么桶排序就是稳定的。
-!!! warning "注"
- 注意区分 **基数排序** 与 **桶排序**
+如果待排序数据是随机生成的,将值域平均分成 $n$ 块的期望时间复杂度是 $O(n)$,证明可以参考算法导论或 [维基百科](https://en.wikipedia.org/wiki/Bucket_sort)。
-算法流程就是记录每一个数出现了多少次,然后从小到大依次输出。
+C++ 代码:
+
+```cpp
+const int N = 100010;
+
+int n, w, a[N];
+vector<int> bucket[N];
+
+void insertion_sort(vector<int> A) {
+ for (int i = 1; i < A.size(); ++i) {
+ int key = A[i];
+ int j = i - 1;
+ while (j >= 0 && A[j] > key) {
+ A[j + 1] = A[j];
+ --j;
+ }
+ A[j + 1] = key;
+ }
+}
+
+void bucket_sort()
+{
+ int bucket_size = w / n + 1;
+ for (int i = 0; i < n; ++i)
+ {
+ bucket[i].clear();
+ }
+ for (int i = 1; i <= n; ++i)
+ {
+ bucket[a[i] / bucket_size].push_back(a[i]);
+ }
+ int p = 0;
+ for (int i = 0; i < n; ++i)
+ {
+ insertion_sort(bucket[i]);
+ for (int j = 0; j < bucket[i].size(); ++j)
+ {
+ a[++p] = bucket[i][j];
+ }
+ }
+}
+```
-一般考虑的是某一范围内的整数,但是计数排序也可以和 [离散化](/misc/discrete) 一起使用,来对浮点数、大整数进行排序。
--- /dev/null
+计数排序是一种复杂度为 $O(n+w)$ 的稳定排序,其中 $w$ 代表待排序数据的值域大小。
+
+计数排序分为三个步骤:
+
+1. 计算每个数出现了几次。
+2. 求出每个数出现次数的前缀和。
+3. 利用出现次数的前缀和,从右至左计算每个数的排名。
+
+伪代码:
+$$
+\begin{array}{ll}
+1 & \textbf{Input. } \text{An array } A \text{ consisting of }n\text{ positive integers no greater than } w. \\
+2 & \textbf{Output. } \text{Array }A\text{ after sorting in nondecreasing order stably.} \\
+3 & \textbf{Method. } \\
+4 & \textbf{for }i\gets0\textbf{ to }w\\
+5 & \qquad cnt[i]\gets0\\
+6 & \textbf{for }i\gets1\textbf{ to }n\\
+7 & \qquad cnt[A[i]]\gets cnt[A[i]]+1\\
+8 & \textbf{for }i\gets1\textbf{ to }w\\
+9 & \qquad cnt[i]\gets cnt[i]+cnt[i-1]\\
+10 & \textbf{for }i\gets n\textbf{ downto }1\\
+11 & \qquad B[cnt[A[i]]]\gets A[i]\\
+12 & \qquad cnt[A[i]]\gets cnt[A[i]]-1\\
+13 & \textbf{return } B
+\end{array}
+$$
+C++ 代码:
+
+```cpp
+const int N = 100010;
+const int W = 100010;
+
+int n, w, a[N], cnt[W], b[N];
+
+void counting_sort()
+{
+ memset(cnt, 0, sizeof(cnt));
+ for (int i = 1; i <= n; ++i) ++cnt[a[i]];
+ for (int i = 1; i <= w; ++i) cnt[i] += cnt[i - 1];
+ for (int i = n; i >= 1; --i) b[cnt[a[i]]--] = a[i];
+}
+```
\ No newline at end of file
- 选择排序: basic/selection-sort.md
- 冒泡排序: basic/bubble-sort.md
- 插入排序: basic/insertion-sort.md
- - 计数排序: basic/bucket-sort.md
+ - 计数排序: basic/counting-sort.md
+ - 基数排序: basic/radix-sort.md
- 快速排序: basic/quick-sort.md
- 归并排序: basic/merge-sort.md
- 堆排序: basic/heap-sort.md
- - 基数排序: basic/radix-sort.md
+ - 桶排序: basic/bucket-sort.md
- 希尔排序: basic/shell-sort.md
- 排序相关 STL: basic/stl-sort.md
- 排序应用: basic/use-of-sort.md