1. 程式人生 > >POj-3784 Running Median-對頂堆

POj-3784 Running Median-對頂堆

問題描述:
For this problem, you will write a program that reads in a sequence of 32-bit signed integers. After each odd-indexed value is read, output the median (middle value) of the elements received so far.
題目大意:
給出一個序列,輸出其子序列 [1,2i1]2i1n 的中位數
AC程式碼:

int main()
{
    int
t; cin >> t; while(t--) { priority_queue<int,vector<int>,less<int> > pq1;//大頂堆 priority_queue<int,vector<int>,greater<int> > pq2;//小頂堆 int id,n; cin >> id >> n; printf("%d %d\n",id,n / 2 + 1);//中位數的總數
int cnt = 0;//每輸出10個數換行一次 for(int i = 1; i <= n; ++i) { int t; cin >> t; if(i % 2 == 1)//如果是第奇數個數 { pq2.push(t);//加入小頂堆 while(!pq1.empty() && pq1.top() > pq2.top())//維護對頂堆的性質,注意第一次大頂堆為空
{ //交換堆頂元素 int t1 = pq1.top(); pq1.pop(); int t2 = pq2.top(); pq2.pop(); pq1.push(t2); pq2.push(t1); } printf("%d ",pq2.top());//輸出小頂堆對頂 cnt++; if(cnt % 10 == 0) cout << endl; } else//第偶數個數放入大頂度 pq1.push(t); } cout << endl; } return 0; }

解決方法:
對於一個序列中的中位數,它左邊的數都比它小,右邊的數都比它大。比較暴力的想法是對每一個要求的序列都進行一次快排,輸出中間的數。這樣非常地慢,而且資訊過多(升序)是一種浪費,對於左邊的數,只要比中位數小就可以了,沒必要有序。
根據數學中的如果一個集合A中最小的數比另一個集合B中最大的數大,則A中的所有數大於B中的所有數。
集合中的最大最小數容易讓我們聯想到堆這種資料結構
最小堆的堆頂要大於最大堆的堆頂,而如果最小堆的大小比最大堆的大小多1的話,最小堆的堆頂就是中位數!
最小堆與最大堆之間的關係在中位數的輸出之前維護即可