zoukankan      html  css  js  c++  java
  • 【基数排序】基数排序

    Algorithm

    Task

    给定 (n) 个整数,请排序后输出

    Limitations

    要求时间复杂度 (O((n + T)log_TA)),空间复杂度 (O(T)) ,其中 (T = 32768)(A) 是序列中最大元素的值

    Solution

    前两天小学妹问我基数排序怎么写,然后我就想起来以前给 ddosvoid 大爷口胡过一个排序,大爷听完说这就是基排,于是就讲给了小学妹(雾),但是我并不确定这是不是基排(((,总之复杂度一致就完事了

    考虑桶排序,将所有数字压入桶中,正向扫描整个桶即可。但是当 (A) 过大时,空间无法承受。

    考虑这样一个事实:对于两个数字 (A,~B),将他们二进制分成两段,先比较前半段,如果大小不同,那么前半段的大小关系即为 (A,~B) 的大小关系,否则比较后半段,二进制后半段较大的一定更大。

    我们发现这个结论可以拓展到将数字分成任意段,于是我们考虑将二进制每 (log_2T) 位分一段,对每段做桶排序,然后合并结果即可。

    那么出现了一个问题:如果从前向后进行比较的话,需要对前面二进制相同的每组数字分别桶排序,这样最多会做 (O(n)) 次桶排序,每次 (O(T)),总复杂度 (O(nT)),当场去世。

    但是考虑一个事实:桶排序是稳定的排序,即如果在某次排序中 (A) 先于 (B) 被扫描到,且本次排序认为 (A = B),则扫描桶的时候 (A) 也会先于 (B) 被扫描。于是我们可以从后往前比较每段,在每次排序前,我们保证已经排过的段的部分的大小关系,在排序结束后,按照新的大小关系从桶中取出。由于之前排过的段的大小已经确定,且桶排序是稳定的,所以这次排序以后已排段的大小关系还是正确的。数学归纳可以证明这样排序的正确性。

    考虑复杂度:一共进行了 (O(log_TA)) 次排序,每次 (O(n + T)) 进行桶排,于是总时间复杂度 (O((n + T) log_TA));空间上,桶排只需要需要 (O(T)) 的空间。

    事实上,如果数据中有负数,只需要正负分开,分别做一遍即可。

    Sample

    【P1177】【模板】快速排序

    Description

    (n) 个值域为 ([1, 10^9]) 的整数,要求排序后输出

    Limitaions

    (n leq 10^5)

    Solution

    板板题

    Code

    #include <cstdio>
    #include <vector>
    
    const int t = 15;
    const int d = 32767;
    const int maxn = 100005;
    const int maxt = 32768;
    
    struct M {
      int pre, pro;
      
      M (const int x = 0) : pre(x >> t), pro(x & d) {}
    };
    M MU[maxn];
    
    int n;
    int ans[maxn];
    std::vector<int>bk[maxt];
    
    void Radix_Sort();
    
    int main() {
      freopen("1.in", "r", stdin);
      qr(n);
      for (int i = 1, x; i <= n; ++i) {
        x = 0; qr(x); MU[i] = M(x);
        ans[i] = i;
      }
      Radix_Sort();
      for (int i = 1; i <= n; ++i) {
        qw((MU[ans[i]].pre << t) | MU[ans[i]].pro, i == n ? '
    ' : ' ', true);
      }
      return 0;
    }
    
    void Radix_Sort() {
      for (int i = 1; i <= n; ++i) {
        bk[MU[ans[i]].pro].push_back(ans[i]);
      }
      int cnt = 0;
      for (auto &i : bk) {
        for (auto u : i) {
          ans[++cnt] = u;
        }
        i.clear();
      }
      for (int i = 1; i <= n; ++i) {
        bk[MU[ans[i]].pre].push_back(ans[i]);
      }
      cnt = 0;
      for (auto &i : bk) {
        for (auto u : i) {
          ans[++cnt] = u;
        }
      }
    }
    
  • 相关阅读:
    2019.8.16
    一种抠环的办法
    [HAOI2015]树上染色
    有关树形背包
    2019.7.27
    有关矩阵快速幂
    2019.7.25
    欧拉函数(转载)
    2019.7.22
    phpstudy集成环境安装redis扩展
  • 原文地址:https://www.cnblogs.com/yifusuyi/p/11252490.html
Copyright © 2011-2022 走看看