leetcode
leetcode 2601 ~ 2650
数据流中的中位数

数据流中的中位数

难度:

标签:

题目描述

English description is not available for the problem. Please switch to Chinese.

代码结果

运行时间: 212 ms, 内存: 25.5 MB


/* 
思路:虽然Java Streams不适用于这个数据流问题的高效实现,但为了演示,我们可以利用List来实现。每次添加数字后,排序整个列表,然后在查找中位数时从列表中获取中位数。这种方式在实际使用中不太高效,因为每次添加数字都需要排序。 
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

class MedianFinderStream {
    private List<Integer> nums;

    public MedianFinderStream() {
        nums = new ArrayList<>();
    }

    public void addNum(int num) {
        nums.add(num);
        nums = nums.stream().sorted().collect(Collectors.toList());
    }

    public double findMedian() {
        int size = nums.size();
        if (size % 2 == 0) {
            return (nums.get(size / 2 - 1) + nums.get(size / 2)) / 2.0;
        } else {
            return nums.get(size / 2);
        }
    }
}

解释

方法:

这个解决方案使用了两个堆,一个最大堆(A)和一个最小堆(B),来维护数据流的中位数。最大堆A负责存储数据流中较小的一半,而最小堆B存储较大的一半。通过确保最大堆的大小总是等于或比最小堆多一个元素,我们可以有效地计算中位数。当添加一个新元素时,我们根据两个堆的大小来决定将其放入哪个堆,并可能需要调整堆,以保持大小的平衡。当寻找中位数时,如果两个堆的大小相同,则中位数是两个堆顶元素的平均值;如果不同,则是元素更多的堆的堆顶元素。

时间复杂度:

O(log n)

空间复杂度:

O(n)

代码细节讲解

🦆
为什么在添加元素时,需要根据两个堆的当前大小来决定元素加入哪个堆?
在添加元素时,根据两个堆的当前大小决定元素加入哪个堆的目的是保持两个堆的大小平衡或仅相差一个元素。这样做可以确保中位数可以快速从堆中被提取。最大堆存储较小的一半元素,而最小堆存储较大的一半元素。通过维持两个堆的平衡,可以确保中位数是可达的,无论元素总数是奇数还是偶数。
🦆
在addNum方法中,为什么先将元素加入一个堆,然后再将顶部元素移动到另一个堆?这样做的目的是什么?
在addNum方法中,先将新元素加入一个堆然后将顶部元素移动到另一个堆的目的是保证两个堆维护的元素仍满足最大堆存储较小一半、最小堆存储较大一半的原则,并且两个堆的大小差不超过1。这种操作可以保证新加入的元素被正确分类,并且维持了堆的结构和大小平衡,从而快速有效地找到中位数。
🦆
在findMedian方法中,如果两个堆的大小相同,为什么返回的中位数是两个堆顶元素的平均值?这与中位数的定义有何关系?
在findMedian方法中,如果两个堆的大小相同,意味着数据流中的元素数量是偶数。根据中位数的定义,在偶数个数的数据集中,中位数是中间两个数的平均值。因此,最大堆的堆顶元素(较小的一半中最大的元素)和最小堆的堆顶元素(较大的一半中最小的元素)的平均值,正是整个数据集中间的两个数的平均值,也就是中位数。
🦆
代码中使用了负数来实现最大堆(A),这种做法的具体原理是什么?
Python中的heapq库只提供了最小堆的实现。为了使用最小堆的特性实现最大堆的效果,代码中将最大堆中的元素以负数形式存储。这样,原本较大的数在取负后变得较小,可以在最小堆的顶部被提取出来,实现了最大值的效果。通过这种方式,我们可以使用heapq库来管理一个最大堆。

相关问题