堆真是一种简单而又神奇的数据结构,以前用它求过前kth的数,现在又可以用两个堆来动态求解中位数。
算法:
构建一个大顶堆和一个小顶堆,分别记为g和l。
假设当前中位数为mid,新读入一个数为tmp,则:
1.如果tmp < mid,则将tmp插入大顶堆,跳到步骤3。
2.如果tmp >= mid,则将tmp插入小顶堆,跳到步骤4。
3.如果大顶堆的元素个数比小顶堆多2(两个堆个数不平衡),则将mid插入小顶堆,弹出大顶堆堆顶元素为新的mid。
4.与步骤3相反,如果小顶堆的元素个数比大顶堆多2,则将mid插入大顶堆,弹出小顶堆堆顶元素为新的mid。
5.如果两个堆元素个数相同,则mid即为当前序列的中位数;否则,中位数为mid和元素个数较多的堆的堆顶元素的平均值,至此算法描述结束。
也有人把这个叫做最大最小堆或者是对顶堆的。
示例代码poj3784:
1 #include <iostream> 2 #include <cstring> 3 #include <cstdio> 4 #include <queue> 5 using namespace std; 6 7 priority_queue<int, vector<int>, less<int> > g; 8 priority_queue<int, vector<int>, greater<int> > l; 9 const int N = 10000; 10 int ans[N]; 11 int p; 12 13 int main () 14 { 15 int t; 16 scanf("%d", &t); 17 while ( t-- ) 18 { 19 int _case, n, mid; 20 scanf("%d%d%d", &_case, &n, &mid); 21 printf("%d %d\n", _case, ( n + 1 ) / 2); 22 p = 0; 23 ans[p++] = mid; 24 while ( !l.empty() ) l.pop(); 25 while ( !g.empty() ) g.pop(); 26 for ( int i = 2; i <= n; i++ ) 27 { 28 int tmp; 29 scanf("%d", &tmp); 30 if ( tmp < mid ) 31 { 32 g.push(tmp); 33 if ( g.size() - l.size() == 2 ) 34 { 35 l.push(mid); 36 mid = g.top(); 37 g.pop(); 38 } 39 } 40 else 41 { 42 l.push(tmp); 43 if ( l.size() - g.size() == 2 ) 44 { 45 g.push(mid); 46 mid = l.top(); 47 l.pop(); 48 } 49 } 50 if ( i & 1 ) 51 { 52 ans[p++] = mid; 53 } 54 } 55 for ( int i = 0; i < p; i++ ) 56 { 57 printf("%d", ans[i]); 58 if ( i % 10 == 9 || i == p - 1 ) 59 { 60 putchar('\n'); 61 } 62 else 63 { 64 putchar(' '); 65 } 66 } 67 } 68 return 0; 69 }