面试题41:数据流中的中位数

题目:

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

分析:

数据是从数据流中读取出来的,数据是无序的,因此,我们需要一个容器将这些数据存储起来。数组,链表,时间复杂度都是O(n)的,一个是查询快,插入慢,另一个是查找慢,插入快。

二叉搜索树,平衡二叉树,都可以已O(logn)的时间复杂度处理数据,二叉搜索树可能会退化成O(n)。

我们可以用堆来实现,建立两个堆,第一个堆是大顶堆,第二个是小顶堆,保证大顶堆所有数据都要小于小顶堆中的数据。需要注意的是,我们要找的是中位数,那么两个堆中元素的个数的差值要小于等于1。当数据总数是偶数的时候,将数据放入小顶堆,否则放入大顶堆。

考虑两个特殊情况。

大顶堆里有数字3,小顶堆里有数字4,这时候,要插入一个数字2,按照前面的规则,会插入小顶堆,可是大顶堆有数据大于小顶堆的数据了。此时,我们把这个数据2插入大顶堆,再把大顶堆中的最大数据3拿出来插入小顶堆,此时就满足要求了。

大顶堆里有数字3,小顶堆里有数字4,5,这时候要插入一个6,这个6会插入大顶堆,可是大顶堆里有元素大于小顶堆了,此时,我们把这个数据6插入小顶堆,再把小顶堆中最小的数据4拿出来了插入大顶堆,此时就满足要求了。

解法:

package com.wsy;

import java.util.PriorityQueue;
import java.util.Random;

public class Main {
    public static PriorityQueue<Integer> minHeap;
    public static PriorityQueue<Integer> maxHeap;

    public static void main(String[] args) {
        readData();
        getMedian();
    }

    public static void readData() {
        minHeap = new PriorityQueue<Integer>((o1, o2) -> o1 - o2);
        maxHeap = new PriorityQueue<Integer>((o1, o2) -> o2 - o1);
        Random random = new Random();
        for (int i = 0; i < 10; i++) {
            int value = random.nextInt(100);
            if ((i & 1) == 1) {// 奇数个,向大顶堆插入
                if (!minHeap.isEmpty() && value > minHeap.peek()) {
                    minHeap.add(value);
                    value = minHeap.poll();
                }
                maxHeap.add(value);
            } else {// 偶数个,向小顶堆插入
                if (!maxHeap.isEmpty() && value < maxHeap.peek()) {
                    maxHeap.add(value);
                    value = maxHeap.poll();
                }
                minHeap.add(value);
            }
        }
    }

    public static void getMedian() {
        int size = minHeap.size() + maxHeap.size();
        if ((size & 1) == 1) {
            System.out.println(maxHeap.peek() * 1.0);
        } else {
            System.out.println((maxHeap.peek() + minHeap.peek()) / 2.0);
        }
    }
}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值