POJ1442 大根堆和小根堆找第k大的数

题意:
给定M个数,每次可以插入序列一个数;再给N个数,表示在插入第几个数后输出一个数,第一次输出序列中最小的,第二次输出序列中第二小的……以此类推,直到输出N个数。

第一次做只用一个优先数列超时了,后来上网看到要用最大堆和最小堆维护做,网上的代码可以把数据都存储在最大堆中,每次加入新数据只要调动一两个就可以了,而我的代码每次加入新数据都得清空队列,相比较要慢多了。

要点:
先设两个优先队列,一个用来存储前k个数,从大到小排列;另一个存储k+1到i个数,从小到大排列,输出时只要输出第一个优先队列的堆顶就是第k大的数了。
每次拿到一个数,先判断第一个优先队列满不满k个,如果不满就直接放入第一个队列。
如果已经满足k个,就与第一个队列堆顶元素比较,如果这个数大于堆顶,说明第k大的数还是堆顶,就把这个数放进第二个队列,
如果这个数小于堆顶,说明第k大的数不是堆顶而是这个新数,那么就把堆顶放进第二个队列,把这个新数放进第一个队列,第一个队列当前的堆顶即第k大的数
总之保证第一个队列中任意一个都大于第二个队列

代码如下:

#include<cstdio>
#include<queue>
#include<functional> //要使用greater模板必须有这个头文件
using namespace std;
const int maxn = 30010;
int a[maxn], u[maxn];

int main()
{
    int i, j, m, n, k, x, ans;
    while (scanf("%d%d", &m, &n) != EOF)
    {
        priority_queue<int, vector<int>, less<int> >que1;//建立一个大根堆
        priority_queue<int, vector<int>, greater<int> >que2;//建立一个小根堆
        for (i = 1; i <= m; i++)
            scanf("%d", &a[i]);
        for (i = 1; i <= n; i++)
            scanf("%d", &u[i]);
        i = 0;  //i代表a数组的下标
        j = k = 1; //k代表第k大的数,j代表u数组的下标
        while(j<=n)
        { 
            if (i == u[j])//判断a数组是否放入u[j]个数
            {
               j++;
               if (que1.size() < k)
               { //如果输入6,6,k变大而que1大小没变,就把que2的堆顶放进que1
                  x = que2.top();
                  que1.push(x);
                  que2.pop();
                }
               ans = que1.top(); //每次输出que1的堆顶就是第k大的数
               printf("%d\n", ans);
               k++; //每次弹出一个数,k就+1
            }
            else
            {
                i++;
                //que1个数小于k,就先把a[i]压入que2,再把que2堆顶压入que1,保证是最小的放入que1
                if (que1.size() < k)
                {
                    que2.push(a[i]);
                    x = que2.top();
                    que2.pop(); //注意这里要去掉堆顶,因为它只是在que2走了个过场,最后还是要放到que1里
                    que1.push(x);
                }
                //如果que1堆顶大于新数,把que1堆顶放到que2中并把que1堆顶去掉,把a[i]放进que1,保证que1个数为1
                else if (que1.top() > a[i])
                {
                    x = que1.top();
                    que1.pop();
                    que1.push(a[i]);
                    que2.push(x);
                }
                else //que1堆顶小于a[i],第k大还是堆顶,直接把a[i]压入que2
                    que2.push(a[i]);
            }
        }
    }
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值