题目:如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

分析:

  • 解法一:可以使用数组来存放插入的数,然后进行排序,再根据数组长度为奇数偶数来确定中位数。注意:需要重写比较器接口。这种方法插入的时间复杂度为O(n),得到中位数的时间复杂度为O(1)。
import java.util.Comparator;
import java.util.ArrayList;
public class Solution {

    private ArrayList<Integer>  arrayList=new ArrayList<>();
    public void Insert(Integer num) {
        arrayList.add(num);
        arrayList.sort(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1-o2;
            }
        });
    }

    public Double GetMedian() {
        int size=arrayList.size();
        if(size%2==0){
            return new Double(arrayList.get(size/2)+arrayList.get((size/2)-1))/2;
        }else{
            return new Double(arrayList.get((size-1)/2));
        }
    }
}
  • 解法二:使用大根堆小根堆来分别保存容器左边和右边的数,最大堆存的是到目前为止较小的那一半数,最小堆存的是到目前为止较大的那一半数,这样中位数只有可能是堆顶或者堆顶两个数的均值。使得保证容器左边的数据都小于容器右边的数据,这样即使左右两边内部的数据没有排序,也可以根据左边最大的数和右边最小的数来得到中位数。为了实现平均分配,可以在数据的总数目为偶数时,把新数据先插入大根堆得到最大的数在把它插入到小根堆,由于插入到小根堆的是原大根堆最大的数字,这样保证小根堆中所有数字都大于大根堆。当数据的总数目为奇数时,把新数据先插入小根堆得到最小的数然后在插入到大根堆,由于插入到大根堆的是原小根堆最小的数,这样也保证小根堆中所有数都小于大根堆。(也可以反过来,偶数的时候先插入小根堆,再插入大根堆,主要的目的就是为了保证两个堆中元素个数之差小于等于1)这种方法的插入的时间复杂度为O(logn),得到中位数的时间复杂度为O(1)。
package Solution51;

import java.util.Comparator;
import java.util.PriorityQueue;

//如何得到一个数据流中的中位数?
// 如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。
// 如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
public class Solution {
    private int count=0;
    private PriorityQueue<Integer> minHeap=new PriorityQueue<>();
    private PriorityQueue<Integer> maxHeap=new PriorityQueue<>(new Comparator<Integer>() {
        @Override
        public int compare(Integer o1, Integer o2) {
            return o2-o1;
        }
    });
    //左边区域为大根堆,右边区域为小根堆
    public void Insert(Integer num) {
        if(count%2==0){ //偶数时,加入小根堆(经大根堆筛选后的解进小根堆)
            //把新数放入大根堆后,得到左边区域最大值后放入右边小根堆,确保右边的值都大于左边的值
            maxHeap.offer(num);
            int filteredMax=maxHeap.poll();
            minHeap.offer(filteredMax);
        }else {  //奇数时,加入大根堆(经小根堆筛选后的解进小根堆)
            minHeap.offer(num);
            int filteredMin=minHeap.poll();
            maxHeap.offer(filteredMin);
        }
        count++;
    }

    public Double GetMedian() {
        if(count%2==0){
            return new Double(minHeap.peek()+maxHeap.peek())/2;
        }else{
            return new Double(minHeap.peek());
        }
    }
}