POJ1442 大根堆和小根堆找第k大的數
阿新 • • 發佈:2018-12-24
題意:
給定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;
}