leetcode295. 数据流的中位数(进阶解析)

传送门

题目: 设计一个支持以下两种操作的数据结构:


```go
void addNum(int num) - 从数据流中添加一个整数到数据结构中。
double findMedian() - 返回目前所有元素的中位数。

原始题目

满足两个特性:
1.大顶堆中最大的数值(堆顶)<=小顶堆中的最小数(堆顶),
  所以大堆堆顶和小堆堆顶中可以存储中位数
  
2.两个堆中元素相差为0,或者为1,不能>1
class MedianFinder {
    Queue<Integer> minHeap, maxHeap;
    public MedianFinder() {
        minHeap = new PriorityQueue<>();
        maxHeap = new PriorityQueue<>((a, b)->(b - a));
    }
    public void addNum(int num) {
        // 优先插入minHeap
        if (minHeap.size() == 0 || num > minHeap.peek()) {
            minHeap.add(num);
        } else {
            maxHeap.add(num);
        }
        if (minHeap.size() > maxHeap.size() + 1) 
            maxHeap.add(minHeap.poll());
        if (maxHeap.size() > minHeap.size() + 1)
            minHeap.add(maxHeap.poll());
    }

    
    public double findMedian() {
        if (minHeap.size() == maxHeap.size()) 
			return 1.0 * (minHeap.peek() + maxHeap.peek()) / 2;
        return minHeap.size() > maxHeap.size() ? minHeap.peek() : maxHeap.peek();
    }
}
优化后的判断插入:利用堆的特性自己调整
 public void addNum(int num) { // minHeap是优先的
	   // 把num加入min堆,利用堆自己调整大小
       minHeap.add(num);    
  
	   // 此时把min堆顶最小值(不一定是上面num) 给max堆,
	   // 平衡两个堆长度,防止min一直增加   
       maxHeap.add(minHeap.poll());
  
       // 在这里真正把元素添加到min堆
       // 如果两个堆元素相等,不用管
       // 如果在上一次调用addNum后,两个堆大小一样,
       // 那么在上面maxHead.add 之后 max比min长
       // 重新 把max堆顶给min堆, 保证min堆比max多一个,或者相等
 	   if (minHeap.size() < maxHeap.size())
           minHeap.add(maxHeap.poll());
     }
    
    public double findMedian() {
        if (minHeap.size() == maxHeap.size()) 
            return 1.0 * (minHeap.peek() + maxHeap.peek()) / 2 ;
        else //如果两个堆长度不等, minHeap长度比maxHeap长度大1
            return minHeap.peek();
    }

进阶一:数据流中所有整数都在 0 到 100 范围内

用一个长度101数组做hash保存每个数字出现的次数。 
然后从0开始累加数组和sum,代表前sum个数,显然中位数 就好找了。
public class MedianFinder {
 
    int[] count; // hash表
    int total;   // 输入数据个数
     public MedianFinder() {
        count = new int[101];
        total = 0;
    }
 
    public void addNum(int num) { // 添加一个数据
        count[num]++;
        total++;
    }
 
    public double findMedian() {  // 找中位数
        if (0 == total % 2) { // total是偶数 找两个中间的数 和除以2
            return (findKthNumber(total / 2) + findKthNumber(total / 2 + 1)) / 2.0;
        } else { // total是奇数  直接返回中间的数 (从1开始数数)
            return findKthNumber(total / 2 + 1);
        }
    }
 
    private int findKthNumber(int k) { // k从1开始数数
        int index = 0;
        for (int i = 0; i < 101; i++) {
            index += count[i];
            // 加上i的个数之后,前面数字个数和>=k,i就是第k个数
            if (index >= k) { 
                return i;
            }
        }
        return -1;
    }
}

时间和空间复杂度均是O(1) 因为对长度是101的数组来说,遍历就是O(1) 长度大小101也就是常数级别。

进阶二:数据流中 99% 的整数都在 0 到 100 范围内

和进阶一同样的思路,只不过将大于100的数除去即可,因为有99%的整数都在0到100范围内,所以中位数一定是0到100范围内的某个数,只需根据大于100的数的个数在进阶一的基础上(统计>100的个数)调整即可。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值