数据流中的中位数

在牛客网上刷题时遇到这道题有新解法,特此来总结下~

这里写图片描述

我开始是这么做的:

import java.util.*;
public class Solution {
    ArrayList<Integer> a =new ArrayList<Integer>();

    public void Insert(Integer num) {
        if(num!=null)
            a.add(num);
    }
    public Double GetMedian() {
        Collections.sort(a);
        int len=a.size();
        int mid=len/2;
        double res=0.0;
        if(len%2!=0)
            {
             res=a.get(mid);
        }
        else
            {
            res=(Double.valueOf(a.get(mid)+a.get(mid-1)))/2;
        }
        return res;//20ms,8664k
    }
}

可是看了别人的答案竟然还可以用堆来做。。。

其实是因为位于容器中的数据被中位数分成了两部分(当输入数据个数为奇数时,中位数为同一个P1=P2;为偶数时,中位数为两个P1,P2)位于容器左边的数据比右边的小。此时p1指向的是左边部分的最大数,而p2指的是右边部分的最小数。

如果能够保证数据容器左边的数都小于右边的数,这样即使左右两边内部的数据没有排序,那么也可以根据左边的最大数以及右边的最小数得到中位数。

  • 关键是如何快速从一个数据容器中找到最大数?
    用最大堆来实现这个数据容器,因为位于堆顶的就是最大数据,最小同理。

所以可以用一个最大堆实现左边数据容器,用最小堆实现右边的数据容器。往对重插入一个数据的时间效率是O(logn)。由于只要O(1)就可以得到位于堆顶的数据,所以得到中位数的时间效率是O(1).

细节问题:

首先保证数据平均分配到两个堆中,因此两个堆中的数据数目之差不能超过1。可以在数据数目为偶数时把新数据插入到最小堆中,否则插入到最大堆中。但是同时还需要保证最大堆里的数据都小于最小堆里的数据,这时需要先把这个新数据插入到最大堆中,接着把最大堆里的最大数拿出来插入到最小堆中。同理需要把一个数据插入到最大堆中。

Java的PriorityQueue 是从JDK1.5开始提供的新的数据结构接口,默认内部是自然排序,结果为小顶堆,也可以自定义排序器,比如下面反转比较,完成大顶堆。

 private int count=0;
    private PriorityQueue<Integer> minHeap= new PriorityQueue<>();
    private PriorityQueue<Integer> maxHeap= new PriorityQueue<Integer>(15,new Comparator<Integer>()
            {
                public int compare(Integer o1,Integer o2)
                {
                    return o2.compareTo(o1);
                }
            });

public void Insert(Integer num)
{
    count++;
    if(count%2==0)
    {
        maxHeap.offer(num);
        minHeap.offer(maxHeap.poll());
    }
    else
    {
        minHeap.offer(num);
        maxHeap.offer(minHeap.poll());
    }

}
public Double GetMedian(){
    if(count%2==0)
        return (minHeap.peek()+maxHeap.peek())/2.0;//或者使用new Double()
    else
        return (double)(maxHeap.peek());//26,8508k
  }
}

这里使用的是把数据为奇数时加入大顶堆,为偶数时加入小顶堆。
制造最大堆和最小堆有数据结构可以直接使用,不用自己整了~

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值