leetcode-295-數據流的中位數-java

題目及測試

package pid295;
/* 數據流的中位數

中位數是有序列表中間的數。如果列表長度是偶數,中位數則是中間兩個數的平均值。

例如,

[2,3,4] 的中位數是 3

[2,3] 的中位數是 (2 + 3) / 2 = 2.5

設計一個支持以下兩種操作的數據結構:

    void addNum(int num) - 從數據流中添加一個整數到數據結構中。
    double findMedian() - 返回目前所有元素的中位數。

示例:

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

進階:

    如果數據流中所有整數都在 0 到 100 範圍內,你將如何優化你的算法?
    如果數據流中 99% 的整數都在 0 到 100 範圍內,你將如何優化你的算法?

*/
public class main {
	
	public static void main(String[] args) {
		MedianFinder obj = new MedianFinder();
		obj.addNum(1);
		obj.addNum(2);
		double param_2 = obj.findMedian();
		System.out.println(param_2);
		obj.addNum(1);
		param_2 = obj.findMedian();
		System.out.println(param_2);
	}
		 

}

解法1(成功,88ms,較慢)

內部有一個鏈表,代表一個有序的鏈表,有head和tail的值爲Integer的min和max

內部有一個TreeMap<Integer, List<Node>> treeMap,Integer爲數字,List<Node>爲該數字對應的節點(可能有多個),新加入的在最後面

每次插入時,先插入treeMap裏對應list的最後一個,然後得到對應節點的前一個,插入鏈表中。

然後移動mid1和mid2到應該對應的位置,結果返回mid1和mid2的中間值

速度是O(log n)

package pid295;

import java.util.ArrayList;
import java.util.List;
import java.util.TreeMap;

class MedianFinder {
	
	class Node{
		int num;
		Node next;
		Node prev;
		public Node(int num){
			this.num = num;
		}
	}
	
	Node head = new Node(Integer.MIN_VALUE);
	Node tail = new Node(Integer.MAX_VALUE);
	
	int size = 0;
	
	Node mid1 = head;
	Node mid2 = tail;
	
	int mid1Index = -1;	
	int mid2Index = -1;
	
	TreeMap<Integer, List<Node>> treeMap = new TreeMap<>();

    /** initialize your data structure here. */
    public MedianFinder() {
    	head.next = tail;
    	tail.prev = head;
    }
    
    // head 1 2  tail
    public void addNum(int num) {
    	Node now = new Node(num);
    	List<Node> list = null;
    	if(!treeMap.containsKey(num)){
    		list = new ArrayList<>();    	
    	}else{
    		list = treeMap.get(num);
    	}
    	list.add(now);
    	treeMap.put(num, list);
    	
    	Node prev = null;
    	
    	if(treeMap.containsKey(num)&&(treeMap.get(num).size()!=1)){
    		list = treeMap.get(num);
    		prev = list.get(list.size()-2);
    	}else if(treeMap.lowerEntry(num)==null){
    		prev = head;
    	}else{
    		list = treeMap.lowerEntry(num).getValue();
    		prev = list.get(list.size()-1);
    	}
    	
    	now.next = prev.next;
    	prev.next = now;
    	now.next.prev = now;
    	now.prev = prev;
    	size ++;
    	
    	if(size == 1){
    		mid1 = now;
    		mid2 = now;
    		mid1Index = 0;
    		mid2Index = 0;
    	}else{
    		// 相同的,新加入的放到最後面
    		if(num<mid1.num){
    			mid1Index++;
    		}
    		if(num<mid2.num){
    			mid2Index++;
    		}
    		// 1 0 0
    		// 2 0 1
    		// 3 1 1
    		
    		int mid1NowIndex = (size -1)/2;
    		int mid2NowIndex = size/2;
    		while(mid1Index != mid1NowIndex){
    			if(mid1Index<mid1NowIndex){
    				mid1Index++;
    				mid1 = mid1.next;
    			}else{
    				mid1Index--;
    				mid1 = mid1.prev;
    			}
    		}
    		while(mid2Index != mid2NowIndex){
    			if(mid2Index<mid2NowIndex){
    				mid2Index++;
    				mid2 = mid2.next;
    			}else{
    				mid2Index--;
    				mid2 = mid2.prev;
    			}
    		}
    	}
    }
    
    public double findMedian() {   	
    	return ((double)mid1.num+(double)mid2.num)/2;
    }
}

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

解法2(別人的)

我們需要明確,對於此題而言,找到median就意味着,我們可以將此數據流分爲兩部分,即第一部分值全部小於median(or =),第二部分值全部大於median(or =)。

所以我們用maxheap存第一部分,並按照倒序存放;minheap來存第二部分,並按照順序排放。

我們先立下一個約定,即maxheap總比minheap多一個,方便於我們之後進行查找。這樣一來,初始第一個值就加入maxheap。

比較即將加入進heap的數字與兩個堆堆頂的數字,若大於maxheap,則需要放進minheap中。

在後面陸續的增加中,我們只需保持兩個heap size之間的關係,不斷進行調節即可。

關於查找,那就只有兩個地方可以找到median:如果是奇數,median肯定在maxheap的堆頂,直接輸出即可;若是偶數,我們需要取出兩個堆各自的堆頂元素,取其均值,再輸出。

class MedianFinder {
    PriorityQueue<Integer> maxHeap;
    PriorityQueue<Integer> minHeap;
    /** initialize your data structure here. */
    public MedianFinder() {
        maxHeap = new PriorityQueue<>((a,b) -> b - a);
        minHeap = new PriorityQueue<>((a,b) -> a - b);
    }
    
    public void addNum(int num) {
        if(maxHeap.size() == 0|| num <= maxHeap.peek()){
            maxHeap.offer(num);
        }else{
            minHeap.offer(num);
        }

        //exchange element because maxHeap must be larger than minHeap.
        if (maxHeap.size() > minHeap.size() + 1){
            minHeap.add(maxHeap.poll());
        }else if (maxHeap.size() < minHeap.size()){
            maxHeap.add(minHeap.poll());
        }
    }
    
    public double findMedian() {
        if(maxHeap.size() != minHeap.size()){
            return maxHeap.peek();
        }else{
            return maxHeap.peek() / 2.0 + minHeap.peek() / 2.0;
        }
    }
}

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章