劍指offer-面試題64:資料流中的中位數
題目:如何得到一個數據流中的中位數?如果從資料流中讀出奇數個數值,那麼中位數就是就是所有數值排序後位於中間的數值。如果從資料流中讀出偶數個數值,那麼中位數就是所有數值排序之後中間兩個數的平均值。
思路:問題本身不難,關鍵在於採取哪種資料結構和演算法。(1)如果用未排序的陣列實現,那麼插入的時間複雜度為O(1),查詢的時間複雜度為O(n)(半快速排序法);(2)如果是排序的陣列,那麼插入的時候要維護順序,插入的時間開銷要大一些,為O(n),查詢時間複雜度為O(1)。(3)用排序的連結串列實現,時間複雜度和排序的陣列情況完全一樣;(4)二叉搜尋樹可以使插入操作更快一點,時間複雜度為O(logn),查詢操作為O(logn)。但是在樹極度不平衡的情況下這兩種操作時間複雜度都是O(n)。(5)還可以用AVL樹實現(說實話關於AVL樹現在腦子裡已經是一片空白,實現起來太複雜)。
作者用到的方法非常巧妙,注意到我們只需要位於中間的兩個數就可以得出中位數,當元素個數為奇數個時可以看成這兩個數一樣。中位數在前半部分它是最大的,右半部分是最小的。我們只要始終知道前半部分數裡的最大的那個數,後半部分裡那個最小的數就可以了,其他的不用關心。這明顯是堆的功用,前半部分用一個最大堆,後半部分建一個最小堆。中位數要保持兩個堆的數目之差不超過1.為了實現平均分配,可以在資料的總數目是偶數時把新資料插入到最小堆中,否則插入最大堆中。
另一個問題是保持最大堆的所有資料都小於最小堆中的資料。如果插入一個數使得總數目和為偶數,根據之前的原則應該插入最小堆中,但是新插入的元素可能比最大堆中的元素小怎麼辦?這就違反了最小堆的資料一定大於最大堆得原則。實際上這裡插入的元素應該是新元素和最大堆中所有元素的最大值,最大堆正好有返回一個最大值的作用,所以新插入的元素先插入最大堆,然後取出最大值再插入最小堆中。如果插入元素使得資料總數目和為奇數,處理方法類似。
程式碼很簡單,只是注意一下堆的實現,對於動態的資料可以用vector來儲存,堆的操作最重要的就是上濾和下濾,可以自己實現,也可以用STL裡的heap來實現,heap操作見: