leetcode295 - Find Median from Data Stream - hard


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.
For example,
[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.

Example:
addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2
Follow up:
1. If all integer numbers from the stream are between 0 and 100, how would you optimize it?
2. If 99% of all integer numbers from the stream are between 0 and 100, how would you optimize it?

1.有序ArrayList, add O(logn + n), find O(1)。
维持有序,增加的时候二分找到插入位置,并插入使数组后移。查找的时候直接用下标random access。

2.有序LinkedList, add O(n + 1), find O(n)。
维持有序,增加的时候遍历过去找到插入位置,并变换指针插入。查找的时候遍历过去查找。

3.一大一小两个堆,add O(logn), find O(1)。
一个maxHeap维持左边一半大的数,堆顶永远可以取到左边一半数里最大的那个;一个minHeap维持右边一半大的数,堆定永远可以取到右边一半数里最小的那个。
那么find就很容易了,根据奇偶选择其中一个的顶或者两者和/2。
加的时候稍微麻烦一点,自己规定好比如左边一半允许比右边一半多一个或一样多,然后插入时根据当前奇偶情况以及要插入的数和两边堆顶的大小关系来适时调换与插入。
细节:
1.取peek的时候注意heap是不是空的的情况,这个会在一开始两个堆都还空着的时候发生。
2.如果两边都偶数要加一加/2的时候,请/2.0而不是/2。小心int削0.5的问题。

实现3:

class MedianFinder {
    
    private PriorityQueue<Integer> left;
    private PriorityQueue<Integer> right;
    
    /** initialize your data structure here. */
    public MedianFinder() {
        left = new PriorityQueue<>(Collections.reverseOrder());
        right = new PriorityQueue<>();
    }
    
    public void addNum(int num) {
        boolean isOdd = (left.size() + right.size()) % 2 == 1;
        if (isOdd && num < left.peek()) {
            right.offer(left.poll());
            left.offer(num);
        } else if (isOdd && num >= left.peek()) {
            right.offer(num);
        } else if (!isOdd && right.size() > 0 && num > right.peek()) {
            left.offer(right.poll());
            right.offer(num);
        } else {
            left.offer(num);
        }
    }
    
    public double findMedian() {
        boolean isOdd = (left.size() + right.size()) % 2 == 1;
        if (isOdd) {
            return left.peek() * 1.0;
        } else {
            // 注意要除以double!
            return (left.peek() + right.peek()) / 2.0;
        }
    }
}

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

猜你喜欢

转载自www.cnblogs.com/jasminemzy/p/9659989.html