1. 程式人生 > >[LeetCode] Find Median from Data Stream 找出資料流的中位數

[LeetCode] Find Median from Data Stream 找出資料流的中位數

Median is the middle value in an ordered integer list. If the size of the list is even, there is no middle value. So the median is the mean of the two middle value.

Examples:

[2,3,4] , the median is 3

[2,3], the median is (2 + 3) / 2 = 2.5

Design a data structure that supports the following two operations:

  • void addNum(int num) - Add a integer number from the data stream to the data structure.
  • double findMedian() - Return the median of all elements so far.

For example:

add(1)
add(2)
findMedian() -> 1.5
add(3) 
findMedian() -> 2

Credits:
Special thanks to @Louis1992 for adding this problem and creating all test cases.

這道題給我們一個數據流,讓我們找出中位數,由於資料流中的資料並不是有序的,所以我們首先應該想個方法讓其有序。如果我們用vector來儲存資料流的話,每進來一個新資料都要給陣列排序,很不高效。所以之後想到用multiset這個資料結構,是有序儲存資料的,但是它不能用下標直接訪問元素,找中位數也不高效。這裡用到的解法十分巧妙,我們使用大小堆來解決問題,其中大堆儲存右半段較大的數字,小堆儲存左半段較小的陣列。這樣整個陣列就被中間分為兩段了,由於堆的儲存方式是由大到小,我們希望大堆裡面的資料是從小到大,這樣取第一個來計算中位數方便。我們用到一個小技巧,就是存到大堆裡的數先取反再存,這樣由大到小存下來的順序就是實際上我們想要的從小到大的順序。當大堆和小堆中的數字一樣多時,我們取出大堆小堆的首元素求平均值,當小堆元素多時,取小堆首元素為中位數,參見程式碼如下:

解法一:

class MedianFinder {
public:

    // Adds a number into the data structure.
    void addNum(int num) {
        small.push(num);
        large.push(-small.top());
        small.pop();
        if (small.size() < large.size()) {
            small.push(-large.top());
            large.pop();
        }
    }

    // Returns the median of current data stream
    double findMedian() {
        return small.size() > large.size() ? small.top() : 0.5 *(small.top() - large.top());
    }

private:
    priority_queue<long> small, large;
};

上述方法是用priority_queue來實現堆功能的,下面我們還可用multiset來實現堆,參見程式碼如下:

解法二:

class MedianFinder {
public:

    // Adds a number into the data structure.
    void addNum(int num) {
        small.insert(num);
        large.insert(-*small.begin());
        small.erase(small.begin());
        if (small.size() < large.size()) {
            small.insert(-*large.begin());
            large.erase(large.begin());
        }
    }

    // Returns the median of current data stream
    double findMedian() {
        return small.size() > large.size() ? *small.begin() : 0.5 * (*small.begin() - *large.begin());
    }

private:
    multiset<long> small, large;
};

參考資料: