1. 程式人生 > >leetcode 295 find median from data stream 找到資料流中的中位數

leetcode 295 find median from data stream 找到資料流中的中位數

思路:

維護一個大根堆和一個小根堆,大根堆中放的是小數,小根堆中放的是大樹,這樣中位數就在大根堆和小根堆中堆頂中間作用

怎麼理解呢,試想最簡單的查詢方法是什麼,是排序,然後找到中位數,複雜度是O(N^2)

我們現在想象一下,中位數不就是排序後前一半數和後一半數中間的數嗎。那我們現在不排序,使用堆,程式碼中用優先順序隊列表示。我們大根堆放小數,大根堆堆頂就是小數中最大的數;小根堆放大樹,小根堆堆頂是大數中最小的數。那麼中位數不就是兩個堆頂中間產生嗎,要麼是前面那個數(大根堆堆頂,當然前提是均衡),要麼是平均數(大根堆小根堆堆頂平均數)

下面java程式碼

class MedianFinder {
    PriorityQueue <Integer> minHeap;
    PriorityQueue <Integer> maxHeap;
    /** initialize your data structure here. */
    public MedianFinder() {
        this.minHeap= new PriorityQueue <>();
        this.maxHeap= new PriorityQueue <>(new Comparator<Integer>() {
            public int compare(Integer o1,Integer o2){
                return o2-o1;
            }
        });
    }
    
    public void addNum(int num) {
        //小根堆中的堆頂大於大根堆中的數
        if(num<findMedian()){
            //當前加入的數比中位數小,應該放前面那個大根堆中去
            maxHeap.add(num);
        }
        else{
            minHeap.add(num);
        }
        //設定是不允許大堆數目比小堆數目多
        if(maxHeap.size()>minHeap.size()){
            minHeap.add(maxHeap.poll());
        }
        //設定允許小堆數目比大堆數目最多多1個結點
        if(minHeap.size()-maxHeap.size()>1){
            maxHeap.add(minHeap.poll());
        }
    }
    
    public double findMedian() {
        if(maxHeap.isEmpty()&&minHeap.isEmpty())
           return 0;
        if(maxHeap.size()==minHeap.size())
           return 1.0*(maxHeap.peek()+minHeap.peek())/2.0;
        else
           return minHeap.peek();//這一個就是說我始終在小根堆裡面多放一個數,如果個數為奇數
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

注意,兄弟們

程式碼中findMedian()後面的return 為什麼是返回小根堆堆頂呢???

因為設定的是在addNum中,就是要小堆始終比大堆最多多放一個,而大堆數目不允許比小堆中數多。這樣有什麼用呢,就是說,中位數要麼是平均數(倆堆數目相等),要麼是小堆中堆頂(奇數個數,小堆多放一個,你說中位數在哪裡)

那有人問了,我可不可以讓大堆多放一個?那是顯而易見的可以啊

下面貼出程式碼

class MedianFinder {
    PriorityQueue <Integer> minHeap;
    PriorityQueue <Integer> maxHeap;
    /** initialize your data structure here. */
    public MedianFinder() {
        this.minHeap= new PriorityQueue <>();
        this.maxHeap= new PriorityQueue <>(new Comparator<Integer>() {
            public int compare(Integer o1,Integer o2){
                return o2-o1;
            }
        });
    }
    
    public void addNum(int num) {
        //小根堆中的堆頂大於大根堆中的數
        if(num<findMedian()){
            //當前加入的數比中位數小,應該放前面那個大根堆中去
            maxHeap.add(num);
        }
        else{
            minHeap.add(num);
        }
        //設定是不允許大堆數目比小堆數目多
        if(maxHeap.size()-minHeap.size()>1){
            minHeap.add(maxHeap.poll());
        }
        //設定允許小堆數目比大堆數目最多多1個結點
        if(minHeap.size()-maxHeap.size()>0){
            maxHeap.add(minHeap.poll());
        }
    }
    
    public double findMedian() {
        if(maxHeap.isEmpty()&&minHeap.isEmpty())
           return 0;
        if(maxHeap.size()==minHeap.size())
           return 1.0*(maxHeap.peek()+minHeap.peek())/2.0;
        else
           return maxHeap.peek();//這一個就是說我始終在小根堆裡面多放一個數,如果個數為奇數
    }
}

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder obj = new MedianFinder();
 * obj.addNum(num);
 * double param_2 = obj.findMedian();
 */

注意到了嗎,我只改了引數罷了,其他都沒動