JZ41 数据流中的中位数

描述

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
示例1
输入:
[5,2,3,4,1,6,7,0,8]
返回值:
"5.00 3.50 3.00 3.50 3.00 3.50 4.00 3.50 4.00 "
说明:
数据流里面不断吐出的是5,2,3…,则得到的平均数分别为5,(5+2)/2,3…

题解

方法一:插入排序法

利用一个list记录不断插入的数字,如果插入后的数字是排序的,那么直接通过下标即可找到中位数所在。这里就采用了插入排序的思想:将新元素插入到一个已排序的序列中,并使得新序列仍有序。实现方法从后向前(从前向后也可)比较,遇到比新元素大(小)的,就交换当前元素和新元素。
java实现如下:

import java.util.ArrayList;
public class Solution {
	//1.插入排序法
    ArrayList<Integer> cap = new ArrayList<>();
    public void Insert(Integer num) {
        cap.add(num);
        for(int i = cap.size() - 1; i >= 1; i --){
            if(cap.get(i) < cap.get(i - 1)){
                int temp = cap.get(i);
                cap.set(i, cap.get(i - 1));
                cap.set(i - 1, temp);
            }
        }
        
    }

    public Double GetMedian() {
        if(cap.size() % 2 == 1) return (double)cap.get((cap.size() / 2));
        return (double)((cap.get(cap.size() / 2) + cap.get(cap.size() / 2 - 1)) / 2.0);
    }
}

方法二:大顶堆、小顶堆记录中位数

可以分别设置一个大顶堆和一个小顶堆,然后交替向大顶堆、小顶堆插入新元素,通过维护两个堆顶,即可随时取出中位数。比如,如果先向大顶堆插入的话,如果插入的数量为奇数,那么maxHeap.peek()就是中位数;如果插入数量为偶数,那么(minHeap.peek() + maxHeap.peek()) / 2就是中位数。
那么该如何维护两个堆呢?这里就用到了中位数的概念,我们可以让小顶堆中任意元素都比大顶堆中任意元素大,即minHeap.peek() > maxHeap.peek(),那么minHeap.peek()和maxHeap.peek()一定是序列最中间的两个数。
代码实现如下:

import java.util.ArrayList;
import java.util.PriorityQueue;
import java.util.Comparator;
public class Solution {
//     1.插入排序法
//     ArrayList<Integer> cap = new ArrayList<>();
//     public void Insert(Integer num) {
//         cap.add(num);
//         for(int i = cap.size() - 1; i >= 1; i --){
//             if(cap.get(i) < cap.get(i - 1)){
//                 int temp = cap.get(i);
//                 cap.set(i, cap.get(i - 1));
//                 cap.set(i - 1, temp);
//             }
//         }
        
//     }

//     public Double GetMedian() {
//         if(cap.size() % 2 == 1) return (double)cap.get((cap.size() / 2));
//         return (double)((cap.get(cap.size() / 2) + cap.get(cap.size() / 2 - 1)) / 2.0);
//     }
    //2.利用大顶堆和小顶堆,让小顶堆所有元素比大顶堆大
    PriorityQueue<Integer> minHeap = new PriorityQueue<>(11);	//默认为小顶堆
    //大顶堆可以用比较器实现
    PriorityQueue<Integer> maxHeap = new PriorityQueue<>(11, new Comparator<Integer>(){
            @Override
            public int compare(Integer o1, Integer o2){
                return o2 - o1;
            }
        });
    int index = 0;
    public void Insert(Integer num) {
        if(index % 2 == 0){
            if(!minHeap.isEmpty() && num > minHeap.peek()){
                maxHeap.offer(minHeap.poll());
                minHeap.offer(num);
            }else{
                maxHeap.offer(num);
            }
        }else{
            if(!maxHeap.isEmpty() && num < maxHeap.peek()){
                minHeap.offer(maxHeap.poll());
                maxHeap.offer(num);
            }else{
                minHeap.offer(num);
            }
        }
        index ++;
        }
    public Double GetMedian() {
        if(index == 0) return 0.0;
        if(index % 2 == 1) return (double)maxHeap.peek();
        //注意返回值为double
        return (maxHeap.peek() + minHeap.peek()) / 2.0;
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值