剑指offer-test63

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

方法1:用java集合PriorityQueue来设置两个堆,一个大顶堆,一个小顶堆来过滤数据。
主要的思想是:因为要求的是中位数,那么这两个堆,大顶堆用来存较小的数,从大到小排列;小顶堆存较大的数,从小到大的顺序排序*,显然中位数就是大顶堆的根节点与小顶堆的根节点和的平均数。
⭐保证:小顶堆中的元素都大于等于大顶堆中的元素,所以每次塞值,并不是直接塞进去,而是从另一个堆中poll出一个最大(最小)的塞值
⭐当数目为偶数的时候,将这个值插入大顶堆中,再将大顶堆中根节点(即最大值)插入到小顶堆中;
⭐当数目为奇数的时候,将这个值插入小顶堆中,再讲小顶堆中根节点(即最小值)插入到大顶堆中;
⭐取中位数的时候,如果当前个数为偶数,显然是取小顶堆和大顶堆根结点的平均值;如果当前个数为奇数,显然是取小顶堆的根节点

 //PriorityQueue 是从JDK1.5开始提供的新的数据结构接口,默认内部是自然排序,结果为小顶堆,就是小的在前面;
//也可以自定义排序器实现Comparator接口,重写compare(object o1,object o2)方法
//默认o1>o2返回正数1,新添加的元素都要比已有的o2大,否则调整堆结构,
//重写后o2>o1返回1,新增加元素o1要比已有的o2小,否则调整堆结构。

//因为用了PriorityQueue,这里不自己写堆排序了。最大堆放比中位数小的值,最小堆放比中位数大的值
//当当前序列为偶数(即大小堆总数为偶),则给最小堆添加一元素,
//此时判断新元素是否比最大堆的堆顶元素小,若是则调整最大堆,把新元素放入最大堆(PriorityQueue会自己调整)
//再把堆顶元素取出放入最小堆(为了保持两堆元素平均分配,因为此时序列为奇数了,下一回合添加的元素是添加到最大堆)。
//后面以此类推。。。

import java.util.Comparator;
import java.util.PriorityQueue;
public class Solution {
    int count;//count用于控制小顶堆和大顶堆个数相差不超过1
    PriorityQueue<Integer> minHeap=new PriorityQueue<Integer>();
    //Creates a PriorityQueue 默认的初始容量设为11
    PriorityQueue<Integer> maxHeap=new PriorityQueue<Integer>(11,new Comparator<Integer>(){
        @Override
        public int compare(Integer o1,Integer o2){
             //PriorityQueue默认是小顶堆,实现大顶堆,需要反转默认排序器
            return o2.compareTo(o1);
        }
    });
    public void Insert(Integer num) {
        count++;
        if((count&1)==0){// 判断偶数的高效写法
        //当数据总数为偶数时,新加入的元素,应当进入小根堆
        //(注意不是直接进入小根堆,而是经大根堆筛选后取大根堆中最大元素进入小根堆)
            //1.新加入的元素先入到大根堆,由大根堆筛选出堆中最大的元素
            if(!maxHeap.isEmpty()&&num<maxHeap.peek()){
                maxHeap.offer(num);
                num=maxHeap.poll();
            }
            //2.筛选后的【大根堆中的最大元素】进入小根堆
            minHeap.offer(num);
        }else{
        //当数据总数为奇数时,新加入的元素,应当进入大根堆
        //(注意不是直接进入大根堆,而是经小根堆筛选后取小根堆中最大元素进入大根堆)
            //1.新加入的元素先入到小根堆,由小根堆筛选出堆中最小的元素
            if(!minHeap.isEmpty()&&num>minHeap.peek()){
                minHeap.offer(num);
                num=minHeap.poll();
            }
            maxHeap.offer(num);
        }
    }

    public Double GetMedian() {
        if(count==0) 
            throw new RuntimeException("no available number!");
        double res;
        //总数为奇数时,大顶堆堆顶就是中位数
        if((count&1)==1)
            res=maxHeap.peek();
        else
            res=(minHeap.peek()+maxHeap.peek())/2.0;
        return res;
    }
}

方法2:

import java.util.*;
public class Solution {
    ArrayList<Integer> res=new ArrayList<>();
    public void Insert(Integer num) {
        res.add(num);
        Collections.sort(res);
    }

    public Double GetMedian() {
        int n=res.size();
        if(n%2==0){
            return Double.valueOf((res.get(n/2)+res.get(n/2-1))/2.0);
        }else{
            return Double.valueOf(res.get(n/2));
        }
        
    }
}

堆知识点了解:
堆(二叉堆):是一种用于实现优先队列模型的数据结构
堆排序:堆积是一个近似完全二叉树的结构(树的所有内部节点都被完全填充,最后一层可以完全填充的或部分填充)
堆排序的平均时间复杂度为Ο(nlogn) 。
堆排序算法步骤:
**步骤一 :**构造初始堆。将无需序列构建成一个堆,根据升序降序需求选择大顶堆或小顶堆;–新插入的数据与其父结点比较
步骤二 :将堆顶元素与末尾元素交换,将最大元素"沉"到数组末端;

**步骤三:**重新调整结构,使其满足堆定义,然后继续交换堆顶元素与当前末尾元素,反复执行调整+交换步骤,直到整个序列有序。

在这里插入图片描述
大顶堆:每个结点的值都大于其左孩子和右孩子结点的值
小顶堆:每个结点的值都小于其左孩子和右孩子结点的值

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值