zoukankan      html  css  js  c++  java
  • 对顶堆学习笔记

    (\)

    对顶堆


    处理动态中位数等问题,灵活运用了堆的性质,本质是维护两个堆。

    大根堆(Q_1):维护集合中较小值的部分的最大值。

    小根堆(Q_2):维护集合中较大值的部分的最小值。

    注意到两个堆中的元素各自是单调的,两个堆间也是单调的。也就是说,(Q_1)中的任何一个元素都不大于(Q_2)中的任何一个元素。

    那么假设高度为权值,两个堆可以形象化的表示成:

    如果两个堆的大小相差不超过(1),较大的那个堆的堆顶必定是中位数(偶数个数时中位数是排序后中间的两个之一)

    (UPD:) 图中的 (Q1)(Q2) 标反了,权值是越高越大。

    (\)

    具体操作


    • 插入

      先找到当前正确的集合插入,比较与堆顶的大小关系,大于小根堆的堆顶就插入大根堆,反之小根堆。

      调整两个堆的大小关系,因为两个堆不管是总体上还是各自内都是满足单调性的,所以每次取更大的堆的堆顶,插入另一个堆,直到两个堆的大小相差不超过(1)

      代码使用的是(STL)(priority\_queue),默认大根堆所以(Q_1)需要通过相反数实现。

      (!q2.size()||x>q2.top())?q1.push(-x):q2.push(x);
      while(q1.size()>q2.size()+1){q2.push(-q1.top());q1.pop();}
      while(q2.size()>q1.size()+1){q1.push(-q2.top());q2.pop();}
      
    • 查询

      直接输出比较大的堆的堆顶就好,注意(Q_1)的输出。

      if(i&1) printf("%d
      ",(q1.size()>q2.size())?-q1.top():q2.top());
      
    • 扩展

      动态(K)大值:维护思想不变,只需要让(Q_1)的大小为(K)即可。

      带删除(K)大值(/)中位数:手写可删堆(/)打标记懒惰删除法

    • 模板

      ([ POJ 3784 ] Running Median) 为例,只有插入操作,每插入到奇数个就输出当前中位数,多组数据。

      #include<cmath>
      #include<queue>
      #include<cstdio>
      #include<cctype>
      #include<cstdlib>
      #include<cstring>
      #include<iostream>
      #include<algorithm>
      #define N 100010
      #define R register
      #define gc getchar
      using namespace std;
      
      inline int rd(){
        int x=0; bool f=0; char c=gc();
        while(!isdigit(c)){if(c=='-')f=1;c=gc();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
        return f?-x:x;
      }
      
      int n,x,cnt;
      priority_queue<int> q1,q2,tmp;
      
      inline void work(){
        cnt=0; x=rd(); n=rd(); q1=tmp; q2=tmp;
        printf("%d %d
      ",x,(n+1)/2);
        for(R int i=1,x;i<=n;++i){
        	x=rd();
          (!q2.size()||x>q2.top())?q1.push(-x):q2.push(x);
          while(q1.size()>q2.size()+1){q2.push(-q1.top());q1.pop();}
          while(q2.size()>q1.size()+1){q1.push(-q2.top());q2.pop();}
          if(i&1){
            printf("%d ",(q1.size()>q2.size())?-q1.top():q2.top());
            if((++cnt)==10&&i!=n) cnt=0,puts("");
          }
        }
        puts("");
      }
      
      int main(){
        int t=rd();
        while(t--) work();
        return 0;
      }
      

    (\)

    一道例题


    对一个集合一共有(N)次操作,计数器(cnt)初始为(0​)

    • (ADD x):把(x)放进该集合。

    • (GET):将(cnt)(1),输出集合种中第(cnt)小的数。

    • (Nin [1,2 imes 10^5])

    (\)

    动态(K)小值,注意题目限制的(Q_2)大小是变化的,每次询问后记得调整好。

    (\)

    #include<cmath>
    #include<queue>
    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cstring>
    #include<iostream>
    #include<algorithm>
    #define N 200010
    #define R register
    #define gc getchar
    using namespace std;
    
    inline int rd(){
        int x=0; bool f=0; char c=gc();
        while(!isdigit(c)){if(c=='-')f=1;c=gc();}
        while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=gc();}
        return f?-x:x;
    }
    
    int n,m,cnt,x,a[N];
    
    priority_queue<int> q1,q2;
    
    int main(){
        n=rd(); m=rd();
        for(R int i=1;i<=n;++i) a[i]=rd();
        x=rd();
        for(R int i=1;i<=n;++i){
            (!q2.size()||a[i]>q2.top())? q1.push(-a[i]):q2.push(a[i]);
            while(q2.size()>cnt) q1.push(-q2.top()),q2.pop();
            while(q2.size()<cnt) q2.push(-q1.top()),q1.pop();
            while(x==i){
                q2.push(-q1.top()); q1.pop();
                printf("%d
    ",q2.top()); 
                x=((++cnt)<m)?rd():-1;
            }
        }
        return 0;
    }
    
  • 相关阅读:
    std thread
    windows更新包发布地址
    How to set up logging level for Spark application in IntelliJ IDEA?
    spark 错误 How to set heap size in spark within the Eclipse environment?
    hadoop 常用命令
    windows 安装hadoop 3.2.1
    windows JAVA_HOME 路径有空格,执行软连接
    day01MyBatisPlus条件构造器(04)
    day01MyBatisPlus的CRUD 接口(03)
    day01MyBatisPlus入门(02)
  • 原文地址:https://www.cnblogs.com/SGCollin/p/9673252.html
Copyright © 2011-2022 走看看