(每日一题)获取数据流中的中位数

1、题目描述

中位数 是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

例如,
[2,3,4] 的中位数是 3
[2,3] 的中位数是 (2 + 3) / 2 = 2.5

示例

输入:

[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...   

2、题解

(1)法一:插入排序法

分析:①Insert 方法:该方法首先检查数组是否为空。如果为空,则直接添加数字。如果不为空,则循环遍历数组找到合适的位置插入数字。这种方法的时间复杂度为 O(n),其中 n 是数组的大小。这是因为我们可能需要遍历整个数组来找到合适的位置。另外,这种方法会改变数组的顺序。

②GetMedian 方法:该方法首先获取数组的大小,然后根据数组的大小确定中位数的位置。这种方法的时间复杂度为 O(1),因为我们直接访问数组的特定位置。注意,这里假设数组已经按照升序排列。如果数组未排序,则需要首先对数组进行排序,这会增加时间复杂度。

代码如下:

public class MedianFinder {

    // 插入排序法
    private ArrayList<Integer> array = new ArrayList<Integer>();

    // 插入数字到已排序的整数数组中  
    public void Insert(Integer num) {
        // 如果数组为空,直接添加数字  
        if (array.isEmpty()) {
            array.add(num);
        } else {
            // 初始化索引i为0  
            int i = 0;
            // 遍历数组直到找到合适的位置插入数字  
            for (; i < array.size(); i++) {
                // 如果当前数字小于等于要插入的数字,跳出循环  
                if (num <= array.get(i)) {
                    break;
                }
            }
            // 在合适的位置插入数字  
            array.add(i, num);
        }
    }

    // 获取数组的中位数  
    public Double GetMedian() {
        // 获取数组的大小  
        int n = array.size();
        // 如果数组大小是奇数,返回中间的数字(向下取整)  
        if (n % 2 == 1) {
            return (double) array.get(n / 2);
        } else {
            // 如果数组大小是偶数,返回中间两个数字的平均值  
            return ((double) array.get(n / 2) + (double) array.get(n / 2 - 1)) / 2;
        }
    }
}

(2)法二:借助堆排序

分析:①insert方法:该方法首先将元素插入到最大堆中。然后,该方法将最大堆中的元素依次弹出,并放入最小堆中。最后,如果最大堆和最小堆的大小不相等,则将最小堆中的最小元素插入到最大堆中。
②getmedian方法:该方法首先判断最大堆的大小是否大于最小堆的大小。如果是,则中位数为最大堆中的堆顶元素。否则,中位数为最大堆和最小堆堆顶元素的平均值。

代码如下:

// 建立大根堆
private PriorityQueue<Integer> maxHeap = new PriorityQueue<>(((o1, o2) -> o2.compareTo(o1)));

// 建立小根堆
private PriorityQueue<Integer> minHeap = new PriorityQueue<>();


// 插入元素
public void insert(Integer num) {

    // 将元素插入到最大堆中
    maxHeap.offer(num);

    // 将最大堆中的元素依次弹出,并放入最小堆中
    while (maxHeap.size() > minHeap.size()) {
        minHeap.offer(maxHeap.poll());
    }

    // 如果最大堆和最小堆的大小不相等,则将最小堆中的最小元素插入到最大堆中
    if (maxHeap.size() < minHeap.size()) {
        maxHeap.offer(minHeap.poll());
    }
}

// 获取中位数
public Double getMedian() {

    // 如果最大堆的大小大于最小堆的大小,则中位数为最大堆中的堆顶元素
    if (maxHeap.size() > minHeap.size()) {
        return (double) maxHeap.peek();
    }

    // 否则,中位数为最大堆和最小堆堆顶元素的平均值
    else {
        return ((double) maxHeap.peek() + (double) minHeap.peek()) / 2;
    }
}

注解:
lambda 表达式 (o1, o2) -> o2.compareTo(o1) 等价于使用匿名内部类实现 Comparator 接口的 compare 方法。在这个比较器中,它通过调用 o2.compareTo(o1) 来比较两个 Integer 对象的大小,从而实现了降序排序。
还原如下:

private PriorityQueue<Integer> min = new PriorityQueue<>(new Comparator<Integer>() {
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2.compareTo(o1);
    }
});

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ai旅人

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值