劍指offer____資料流中的中位數
阿新 • • 發佈:2018-11-17
如何得到一個數據流中的中位數?如果從資料流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從資料流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。我們使用Insert()方法讀取資料流,使用GetMedian()方法獲取當前讀取資料的中位數。
/* 如何得到一個數據流中的中位數?如果從資料流中讀出奇數個數值,那麼中位數就是所有數值排序之後位於中間的數值。如果從資料流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。 對於資料流,對應的就是線上演算法了,一道很經典的題目就是在1億個數中找到最大的前100個數,這是一道堆應用題,找最大的前100個數,那麼我們就建立一個大小為100的最小化堆,每來一個元素就與堆頂元素比較,因為堆頂元素是目前前100大數中的最小數,前來的元素如果比該元素大,那麼就把原來的堆頂替換掉。 那麼對於這一道題呢?如果單純的把所有元素放到一個數組裡,每次查詢中位數最快也要O(n),綜合下來是O(n^2)的複雜度。我們可以利用上面例子中的想法,用一個最大堆來維護當前前n/2小的元素,那麼每次找中位數只到取出堆頂就可以了。但是,有一個問題,資料要動態增長,有可能之前被替換掉的元素隨著元素的增加又跑回來了,所以我們不能單純得向上題一樣把元素丟掉,我們可以再用一個最小化堆來存前n/2大的元素。 */ class Solution { private: vector<int> min; //陣列中的後一半元素組成一個最小化堆 vector<int> max; //陣列中的前一半元素組成一個最大化堆 public: void Insert(int num) { if(((min.size()+max.size()) & 1) == 0) { //偶數資料的情況下,則在最小堆中插入元素 if(max.size() > 0 && num < max[0]) { max.push_back(num); push_heap(max.begin(), max.end(), less<int>()); num=max[0]; pop_heap(max.begin(), max.end(), less<int>()); max.pop_back(); } min.push_back(num); //把前一半找到的最大值放到後一半中 push_heap(min.begin(), min.end(), greater<int>()); } else { if(min.size() > 0 && num > min[0]) { //奇數資料的情況下,則在最大堆中插入元素 min.push_back(num); push_heap(min.begin(), min.end(), greater<int>()); num=min[0]; pop_heap(min.begin(), min.end(), greater<int>()); min.pop_back(); } max.push_back(num); //把後一半找到的最大值放到前一半中 push_heap(max.begin(), max.end(), less<int>()); } } double GetMedian() { int size=min.size() + max.size(); if(size==0) return -1; double median = 0; if((size&1) != 0) { median = (double) min[0]; } else { median = (double) (max[0] + min[0]) / 2; } return median; } };