Acwing54.数据流中的中位数——使用大根堆和小根堆

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

示例:

输入:1, 2, 3, 4
输出:1,1.5,2,2.5
解释:每当数据流读入一个数据,就进行一次判断并输出当前的中位数。

算法思路:
本题,一开始我想到是时使用指针的方式,预先定义一个index指针指向第一个元素,当输入数据流的个数是奇数的时候,那么就不移动指针,且指针指向的元素就为中位数;当输入数据流的个数是偶数的时候,先移动指针+1,然后当前index指向的元素和index-1的元素相加求平均即为中位数;使用此方式在数据集较小的情况下能够Accept,但是数据集一大,就会limit时间限制。
于是采用第二种方式,使用大根堆和小根堆来存储元素,大根堆存储小于或等于中位数的元素,小根堆存储大于中位数的元素,当数据流中元素个数为偶数时,大根堆和小根堆的大小是一样的;当数据流中元素个数为奇数时,大根堆=小根堆+1;那么就可以当偶数是将大根堆的结点和小根堆的结点元素取出求平均即可;当元素个数为奇数时,则取大根堆的根结点。

算法实现:
1、使用大小根堆方式(建议采用):

class Solution {
    
    
    //使用大根堆和小根堆来存储元素
    //大根堆,父结点的元素大于左右子结点,存储小于等于中位数的数
    PriorityQueue<Integer> small = new PriorityQueue<>((a,b)->b-a);
    //小根堆,父结点的元素小于左右子结点,存储大于中位数的数
    PriorityQueue<Integer> large = new PriorityQueue<>((a,b)->a-b);
    
    public void insert(Integer num){
    
    
        //当数据流中数字个数为偶数时,大根堆和小根堆大小一样
        //当数据流当中的数字个数为奇数时,大根堆的大小=小根堆大小+1
        if(small.isEmpty()||num<small.peek()){
    
    
            //当大根堆为空或者数据流当前的元素小于大根堆元素
            //大根堆使用offer()进行元素的添加与删除
            small.offer(num);
            if(small.size()>large.size()+1){
    
    
                //如果大根堆中加入的元素使得当前大根堆与小根堆大小不府
                //将大根堆最大的元素加入到小根堆当中
                large.offer(small.poll());
            }
        }else{
    
    
            //加入的元素较大
            //加入到小根堆当中
            large.offer(num);
            //小根堆只能大于或者小于大根堆中的元素
            if(large.size()>small.size()){
    
    
                small.offer(large.poll());
            }
        }
    }
    public Double getMedian(){
    
    
        //返回中位数
        if(small.size()==large.size()){
    
    
            //表示数据流中的元素为偶数
            double num1 = small.peek();
            double num2 = large.peek();
            return (num1+num2)/2;
        }else{
    
    
            //数据流中奇数
            double num = small.peek();
            return num;
        }
    }
}

2、使用指针

class Solution {
    
    
    //定义一个集合用来存储元素
    List<Integer> list = new ArrayList<>();
    //定义一个指针用来查找中间两个元素
    int index = 0;
    double res = 0.0;
    public void insert(Integer num) {
    
    
        //往集合当中添加元素
        list.add(num);
    }
    //所有数字排序之后
    public Double getMedian() {
    
    
        int[] arr = new int[list.size()];
        for(int i=0;i<arr.length;i++){
    
    
            arr[i] = list.get(i);
        }
        Arrays.sort(arr);
        for(int i=0;i<arr.length;i++){
    
    
            list.set(i,arr[i]);
        }
        //该方法是用来获取中间两个数的平均值
        //当奇数次不移动index
        if(list.size()%2==1){
    
    
            res = list.get(index);
            return res;
        }else{
    
    
            //偶数次需要移动index
            // System.out.println(list.size());
            //此时移动index指针指向下一位
            index++;
            double i1 = list.get(index);
            double i2 = list.get(index-1);
            res = (i1+i2)/2;
            return res;
        }
     }
}     

猜你喜欢

转载自blog.csdn.net/weixin_56068397/article/details/123377077