1. 程式人生 > >POJ1442 大根堆和小根堆找第k大的數

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; }