剑指Offer——数据流中的中位数

题目描述:如何在数据流中得到中位数?对于排序好的数据流,如果数据流中的元素个数是奇数,那么中位数就是中间那个数,如果数据流中元素的个数是偶数,那么中位数就是数据流中的中间两个数的平均数,有了这个分析,我们很容易想到要对数据流中的元素进行排序。那么现在要考虑的就是使用什么容器来存储数据流中的数字,显然这里我们可以用数组,也可以用链表,那么我们来分析一下这两种容器使用时的时间复杂度:

①数组:数组又可以分为排序的数组和非排序的数组

◇排序的数组:在排序的数组中插入一个数,可能要移动n个数,因此插入的时间复杂度是O(n);

                      在排序的数组中找到中间的数的时间复杂度是O(1);

◇非排序的数组:在非排序的数组中插入一个数的时间复杂度是O(1);

                          在非排序的数组中找到中位数的时间复杂度是O(n),因为要逐个对比直到找到中间的数;

②链表:排序的链表也可以作为一种选择

◇排序的链表:在排序的链表中,在合适的位置插入一个数的时间复杂度是O(n);

                       在排序的链表中,查找中间的数的时间复杂度是O(n)。但是如果是用一个指针,始终指向链表的中间位置,那么,在排序的链表中查找中位数的时间复杂度为O(1);

③还有一种思路是对于一颗平衡的搜索二叉树来说,如果把平衡的定义改为左子树的节点个数和又子树的节点个数相差不超过1,那么这样的平衡搜索二叉树的根节点就是中位数。但是要时间这样的平衡搜索二叉树并不容易。

④还有一种思路就是对于中位数m来说,其左边的数都小于m,其右边的数都大于m,因此如果我们能够把这些数据流中的数平均的分为两个部分,一个部分(A)的所有数都小于另一个部分(B)的所有数,那么我们只需要知道A的最大值和B的最小值即可。可以在O(1)时间内找到最大值和最小值的容器,很容易想到最大堆和最小堆,在Java中,可以用Collection接口的一个实现类PriorityQueue来作为最大堆、最小堆的容器。

PriorityQueue是Queue接口的子接口Dequeue的实现类,在PriorityQueue接口中实现了Comparator接口,默认是从小到大的排序,PriorityQueue的入队操作(add(), offer())和出队操作(peek(), remove(), poll())都是针对队列头进行操作,因此PriorityQueue本身是一个最小堆的实现。

那么怎么实现最大堆呢,只要重写PriorityQueue中的Comparator的compare方法即可,将Comparator的排序方式变为从小到大的排序方式就实现的最小堆。

有了这个思路,就可以很容易的想到,当元素是第奇数个数时,将元素放进最大堆,否则放进最小堆,但是这不是直接放进去,如果当前元素是第奇数个数,那么该把他放进最大堆,但是如果他比最小堆的最小值大,那么就不能保证最大堆中的元素全部小于最小堆中的元素了,所以,应该先比较钙元素和最小堆中最小的元素,把较小的元素放进最大堆;放进最小堆的元素同理。

实现代码如下:

import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
    PriorityQueue<Integer> min = new PriorityQueue<>();
    PriorityQueue<Integer> max = new PriorityQueue<Integer>(15,new Comparator<Integer>(){
        @Override
        public int compare(Integer a, Integer b){
            return b-a;
        }
    });
    int count = 0;
    public void Insert(Integer num) {
    //根据分析可以知道,在Java中,使用priorityQueue作为最大堆和最小堆的容器,因为priority实现的comparator,排序顺序默认从小到大
        if(count%2 == 0){
            //当数据流中元素个数为偶数时,将元素插入最大堆,但是插入时,要和最大堆的最大值比较,当插入的元素比最大堆的最大值还大,那
            //么将元素插入最小堆并将最小堆的最小值插入最大堆中
            min.offer(num);
            max.offer(min.poll());
        }
        else{
            //当数据流中元素个数为奇数时,将元素插入最小堆,但是插入时,要和最小堆的最大值比较,当插入的元素比最小堆的最大值还小,那
            //么将元素插入最大堆并将最大堆的最大值插入最小堆中
            max.offer(num);
            min.offer(max.poll());
        }
        count++;
    }

    public Double GetMedian() {
        if(count%2 == 1){
            //当count为奇数时,中位数是最大堆的最大值
            return new Double(max.peek());
            
        } 
        else{
            //当count为偶数时,中位数是最大堆的最大值和最小堆的最小值的平均数
            return new Double(max.peek() + min.peek())/2;
        }
    }


}


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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值