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
    • 加入 11 1 2 ? 3 4 5\(a\) 变为0 2 4 5 6
    • 加入 31 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;
}
posted @ 2024-01-28 22:08  hhhqx  阅读(32)  评论(0编辑  收藏  举报