剑指offer-52.数据流中的中位数(214)

52.数据流中的中位数(214)
  • 题目描述:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。

  • 思路:(1)使用快排的分区函数找出中位数。(2)利用最大堆和最小堆。

  • 代码:

    package _52.数据流中的中位数;
    
    import java.util.LinkedList;
    /**
     * 如何得到一个数据流中的中位数?
     * 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
     * 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
     * 我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
     * @author Administrator
     *
     */
    public class StreamMedian {
    	private static LinkedList<Integer> list = new LinkedList<>();
    	//向数据流中插入数据
        public static void Insert(Integer num) {
        	list.add(num);
        }
        
        //获取数据流中的中位数
        public static Double GetMedian() {
        	if(list.size() == 0) return -1.0;
        	
            int left = 0;
            int right = list.size() - 1;
            int index = paritition(left,right);
            int mid = list.size() >>> 1;
            while(index != mid){
            	if(index > mid){
            		index = paritition(left,index-1);
            	}
            	if(index < mid){
            		index = paritition(index+1,right);
            	}
            }
            //判断链表的长度是奇数还是偶数
            if((list.size() & 1) == 1){//奇数
            	return list.get(index)*1.0;
            }
            else{//偶数
            	return (list.get(mid)+list.get(mid-1))/2.0;
            }
           
        }
        
        /**
         * 快排的分区函数[小,pivot,大]
         * @param left
         * @param right
         * @return
         */
        public static int paritition(int left,int right){
        	Integer pivot = list.get(left);
        	while(left < right){
        		while(left<right && list.get(right) >= pivot)
        			right--;
        		list.set(left, list.get(right));
        		while(left<right && list.get(left) < pivot)
        			left++;
        		list.set(right, list.get(left));
        	}
        	list.set(right, pivot);
        	return right;
        }
        
        public static void main(String[] args) {
    		for(int i = 1; i < 20; i++){
    			Insert(i);
    			Double median = GetMedian();
    			System.out.println(median);
    		}
    	}
    }
    
package _52.数据流中的中位数;

import java.util.ArrayList;

/**
 * 使用最大堆、最小堆解决
 * @author Administrator
 *
 */
public class MedianFinder2 {
	private static ArrayList<Integer> maxHeap = new ArrayList<>();//大根堆
	private static ArrayList<Integer> minHeap = new ArrayList<>();//小根堆
     
    public static void Insert(Integer num) {
        //1.如果大根堆为空,直接加入大根堆中
    	if(maxHeap.size() == 0 || num <= maxHeap.get(0)){
    		maxHeap.add(num);
    		for(int i = maxHeap.size()/2 ; i >= 0; i--){
    			maxShif(i);
    		}
    	}
    	//2.如果大根堆不为空,判断num和大根堆中最大元素的大小,如果num小于大根堆中的最大元素,则加入大根堆
//    	else if(num < maxHeap.get(0)){
//    		maxHeap.add(num);
//    		for(int i = maxHeap.size()/2 ; i >= 0; i--){
//    			maxShif(i);
//    		}
//    	}
    	//3.如果num大于大根堆中的最大值,看是否能加进小根堆中
    	//3.1 如果小根堆为空则加入小根堆,
    	else if(minHeap.isEmpty() || num > minHeap.get(0)){
    		minHeap.add(num);
    		for(int i = minHeap.size()/2 ; i >= 0; i--){
    			minShif(i);
    		}
    		System.out.println(minHeap.toString());
    	}
    	//3.2 如果num大于小根堆的最小值,则加入小根堆
//    	else if(num > minHeap.get(0)){
//    		minHeap.add(num);
//    		for(int i = minHeap.size()/2 ; i >= 0; i--){
//    			minShif(i);
//    		}
//    	}
    	//3.3 如果num小于小根堆的最小值,则加入大根堆
    	else if(num <= minHeap.get(0)){
    		maxHeap.add(num);
		    for(int i = maxHeap.size()/2 ; i >= 0; i--){
			maxShif(i);
		    }
		   System.out.println(maxHeap.toString());
		}
    	
    	
    	//平衡两个堆中的元素
    	if (minHeap.size() > maxHeap.size() + 1){
    		maxHeap.add(minHeap.remove(0));
    	}
    	else if(maxHeap.size() > minHeap.size() + 1){
    		minHeap.add(maxHeap.remove(0));
    	}
    	for(int i = maxHeap.size()/2 ; i >= 0; i--){
			maxShif(i);
		    }
    	for(int i = minHeap.size()/2 ; i >= 0; i--){
			minShif(i);
		}
    	
    }

    public static Double GetMedian() {
    	if(maxHeap.isEmpty() && minHeap.isEmpty()) 
    		return -1.0;
    	
        if(maxHeap.size() == minHeap.size()){
        	return (maxHeap.get(0) + minHeap.get(0))/2.0;
        }
        else{
        	return maxHeap.size() > minHeap.size() ? maxHeap.get(0)*1.0 : minHeap.get(0)*1.0;
        }
    }
    
    public static void maxShif(int low){
    	int i = low;
    	int j = 2*i + 1;
    	int high = maxHeap.size() - 1;
    	while(j <= high){
    		if((j<high) && maxHeap.get(j) < maxHeap.get(j+1))
    			j++;
    		if(maxHeap.get(i) < maxHeap.get(j)){
    			Integer tmp = maxHeap.get(i);
    			maxHeap.set(i,  maxHeap.get(j));
    			maxHeap.set(j, tmp);
    			i = j;
    			j = i*2+1;
    		}
    		else break;
    	}
    }
    public static void minShif(int low){
    	int i = low;
    	int j = 2*i + 1;
    	int high = minHeap.size() - 1;
    	while(j <= high){
    		if((j<high) && minHeap.get(j) > minHeap.get(j+1))
    			j++;
    		if((j<high) && minHeap.get(i) > minHeap.get(j)){
    			Integer tmp = minHeap.get(i);
    			minHeap.set(i, minHeap.get(j));
    			minHeap.set(j, tmp);
    			i = j;
    			j = 2*i + 1;
    		}
    		else break;
    	}
    }
    
     public static void main(String[] args) {
    	 for(int i = 1; i < 20; i++){
 			Insert(i);
 			Double median = GetMedian();
 			System.out.println(median);
 		}
	}
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值