O(n) 排序 - 基数排序
O(n) 排序 —— 基数排序
基数排序
来举个例子:
- 我们需要对 \(c\)
1 4 5 2 3 1 3
进行排序 - 定义 \(a\)
2 3 5 6 7
,第 \(i\) 个表示序列中有 \(a_i\) 个小于 \(i\) 的元素。 - 从左向右扫一遍,序列的确定部分如下
- 加入
1
,? 1 ? ? ? ? ?
,\(a\) 变为1 3 5 6 7
- 加入
4
,? 1 ? ? ? 4 ?
,\(a\) 变为1 3 5 5 7
- 加入
5
,? 1 ? ? ? 4 5
,\(a\) 变为1 3 5 5 6
- 加入
2
,? 1 2 ? ? 4 5
,\(a\) 变为1 2 5 5 6
- 加入
3
,? 1 2 ? 3 4 5
,\(a\) 变为1 2 4 5 6
- 加入
1
,1 1 2 ? 3 4 5
,\(a\) 变为0 2 4 5 6
- 加入
3
,1 1 2 3 3 4 5
,\(a\) 变为0 2 3 5 6
- 加入
相信你看完例子就已经懂了基数排序。
进阶基数排序
我们注意到基数排序可以是稳定排序,且时间复杂度是 \(O(n + max^{n}_{i-1}{c_i})\)。
不带 $\log $ 看起来就很妙。
但是这个 \(max^{n}_{i-1}{c_i}\) 就很烦。
于是!我们想到将 \(c_i\) 分成两半,变为两个关键字排序。
这样还可以基数排序吗?当然可以!
因为基数排序可以是稳定的,所以对第二关键字排一遍,接着对第一关键字排一遍。
代码实现起来有一些小小的细节需要注意!
#include <bits/stdc++.h>
using namespace std;
using LL = long long;
using PII = pair<int, int>;
const int MAXN = 1e5 + 3, MAXW = 1e5 + 3;
int n;
int r[MAXW];
PII a[MAXN], b[MAXN];
int main(){
ios::sync_with_stdio(0), cin.tie(0);
cin >> n;
for(int i = 1, w; i <= n; i++){
cin >> w, a[i] = {w / 100000, w % 100000};
}
for(int i = 1; i <= n; i++) r[a[i].second]++;
for(int i = 0; i <= 1e5; i++) r[i] += r[i - 1];
for(int i = n; i >= 1; i--) b[r[a[i].second]] = a[i], r[a[i].second]--;
for(int i = 0; i <= 1e5; i++) r[i] = 0; // 清空
for(int i = 1; i <= n; i++) r[b[i].first]++;
for(int i = 1; i <= 1e5; i++) r[i] += r[i - 1];
for(int i = n; i >= 1; i--) a[r[b[i].first]] = b[i], r[b[i].first]--;
for(int i = 1; i <= n; i++){
cout << a[i].first * 100000 + a[i].second << " ";
}
return 0;
}