从零学算法295

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

例如 arr = [2,3,4] 的中位数是 3 。

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

实现 MedianFinder 类:

MedianFinder() 初始化 MedianFinder 对象。

void addNum(int num) 将数据流中的整数 num 添加到数据结构中。

double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入

"MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"

\[\], \[1\], \[2\], \[\], \[3\], \[\]

输出

null, null, null, 1.5, null, 2.0

解释

MedianFinder medianFinder = new MedianFinder();

medianFinder.addNum(1); // arr = [1]

medianFinder.addNum(2); // arr = [1, 2]

medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)

medianFinder.addNum(3); // arr[1, 2, 3]

medianFinder.findMedian(); // return 2.0

  • 他人解法:直接两个优先队列开始表演魔法(我这辈子估计也想不出这解法)。首先定义两个堆,各存放列表中一半的数据,小顶堆 A 存放较大部分,大顶堆 B 存放较小部分,优先往小顶堆存放元素(即,列表元素个数为奇数时,A 多存一个)。首先根据这个定义,当元素个数为奇数个时,我们就应该从 A 中取中位数,因为 A 多存的那个数就是中位数,为了让 A 堆顶元素就是中位数,我们就需要让 A 保持升序。当元素个数为偶数个,我们需要从 A,B 的堆顶各自取出一个数然后求平均数,那么 B 的堆顶如果需要是第二大的中位数,那 B 就需要保持降序。定义堆与取中位数已经解决了,剩下的 addNum 可以说是神来之笔。当 A B 元素个数不相同时,那么就该向 B 添加数了,但是为此时并不是直接添加到 B,因为我不确定我直接添加以后还能否保持(A 存放较大部分,B 存放较小部分)这个特性。所以我先把他加到 A,因为 A 是升序存储,所以堆顶的元素一直是最小的,把添加的数加到 A 以后再弹出堆顶的数给 B,那么此时弹出的数一定是小于 A 中此时所有的数的了,也就是说,我保持了 A 中的都是较大数。同理,当 AB 元素个数相同时,我要先给 A 加元素,但我先往 B 中添加,由于 B 是降序优先队列,所以 B 的堆顶元素是 B 的最大元素,我把 B 的堆顶元素弹出给 A,我就保证了 B 的所有数都小于弹给 A 的数。这样一来,我就保证了 A 只存放较大部分的数,B 只存放较小部分的数。
  • 或者说一个数被添加时,先看看是加给谁,如果是 A 中多了一个名额,因为 A 只收强者,所以它就需要先打败 B 中的所有数证明自己,否则只能把这个新的名额给 B 中此时的最强者了。而如果 B 中多了一个名额,他当然不想去弱者队,它就先去 A 中看看有没有它能打败的,有的话就把 A 中此时最弱者挤到了 B,否则只能它进 B 了。
java 复制代码
  // 小顶堆,存放较大部分
  public Queue<Integer> A = new PriorityQueue<>();
  // 大顶堆,存放较小部分
  public Queue<Integer> B = new PriorityQueue<>((x,y)->y-x);
  /** initialize your data structure here. */
  public MedianFinder() {

  }
  
  public void addNum(int num) {
      if(A.size() != B.size()) {
      	// B 有新名额了,先去 A 中挑战一下
          A.add(num);
          // 最后有没有留在 A 我就不知道了,但是 A 中此时最弱的要去 B 的新名额了
          B.add(A.poll());
      } else {
      	// A 有新名额了,先去 B 中证明自己是 B 中最强的
          B.add(num);
          // 最后有没有进 A 我也不知道,但是一定是 B 中最强者进 A 的新名额了
          A.add(B.poll());
      }
  }
  
  public double findMedian() {
      return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;
  }
相关推荐
南玖yy2 分钟前
数据结构C语言练习(两个队列实现栈)
c语言·数据结构·算法
明朝百晓生18 分钟前
【强化学习】【1】【PyTorch】【强化学习简介优化框架】
算法
loser~曹24 分钟前
基于快速排序解决 leetcode hot215 查找数组中第k大的数字
数据结构·算法·leetcode
Dream it possible!29 分钟前
LeetCode 热题 100_打家劫舍(83_198_中等_C++)(动态规划)
c++·算法·leetcode·动态规划
okok__TXF33 分钟前
Mybatis源码分析
java·后端·mybatis
zhouziyi070135 分钟前
【蓝桥杯14天冲刺课题单】Day 8
c++·算法·蓝桥杯
SylviaW0839 分钟前
python-leetcode 62.搜索插入位置
数据结构·算法·leetcode
白云如幻1 小时前
【Java】Hibernate的一级缓存
java·hibernate
eternal__day1 小时前
Spring Boot 快速入手
java·spring boot·后端·spring·java-ee·maven
爱的叹息1 小时前
Spring Boot中事务状态(TransactionStatus)的核心信息及常见应用场景
java·spring boot·后端