# Priority Queue# Heap# Greedy# Online Algorithm

d713 - 中位数

🔗 前往 ZeroJudge 原題

題目描述

題目要求我們處理一個整數序列,並在每次讀入一個新的整數後,輸出到目前為止所有整數的中位數。中位數的定義是排序後位於中間的數,如果數字個數為偶數,則取中間兩個數的和的整數部分。

解題思路

這題可以使用兩個優先佇列(堆)來解決。一個最大堆(p)用於存儲序列中較小的一半數字,一個最小堆(q)用於存儲序列中較大的一半數字。

維持兩個堆的平衡,使得它們的大小相差不超過 1。當讀入一個新的數字時,將其插入到合適的堆中。如果兩個堆的大小不平衡,則將堆頂元素從較大的堆移動到較小的堆,以保持平衡。

中位數的計算取決於序列中數字的個數是奇數還是偶數。如果數字個數為奇數,則中位數是較大堆的堆頂元素。如果數字個數為偶數,則中位數是兩個堆頂元素之和的整數部分。

程式碼中 st 變數用於區分不同的輸入情況,但邏輯上可以簡化。核心思想是維護兩個優先佇列,並在每次插入新元素後調整它們,以確保它們始終包含序列中較小和較大的一半元素。

複雜度分析

  • 時間複雜度: O(N log N),其中 N 是輸入數字的個數。因為每次插入和刪除操作都需要 O(log N) 的時間,而我們需要執行 N 次這些操作。
  • 空間複雜度: O(N),因為我們需要存儲所有輸入數字在兩個優先佇列中。

程式碼

#include <iostream>
#include <queue>
using namespace std;
long long n,st,k,m;
priority_queue <long long,vector <long long>,greater <long long> > q;//min
priority_queue <long long> p;//max
int main(){
	cin.tie(0); ios::sync_with_stdio(false);
	while(cin >> k){
		//cout << st << " ";
		if(st==0){
			cout << k << "\n";
			n=k;
		}
		else if(st==1){
			cout << (k+n)/2 << "\n";
			m=k;
			p.push(min(n,m));//1
			q.push(max(n,m));//3
		}
		else if(st==2){
			if(k>q.top()){
				q.push(k);//3 4
				k=q.top();//k = 3
				q.pop();// 4
			}
			else if(k<p.top()){
				p.push(k);
				k=p.top();
				p.pop();
			}
			cout << k << "\n";
			n=k;
		}
		else if(st%2){
			//1 3 4 60
			if(k>=n){
				q.push(k);//4 60
				m=(n+q.top())/2;
			}
			//2 3 4 1
			else{
				p.push(k);
				m=(n+p.top())/2;
			}
			cout << m << "\n"; 
		}
		else{
			if(q.size()>p.size()){
				p.push(n);
				if(k>q.top()){
					q.push(k);
					k=q.top();
					q.pop();
				}
				p.push(k);
				n=p.top();
				p.pop();
			}
			else{
				q.push(n);
				if(k<p.top()){
					p.push(k);
					k=p.top();
					p.pop();
				}
				q.push(k);
				n=q.top();
				q.pop();
			}
			cout << n << "\n";
		}
		++st;
	}
}

Discussion