从零学算法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;
  }
相关推荐
在坚持一下我可没意见1 分钟前
Spring IoC 入门详解:Bean 注册、注解使用与 @ComponentScan 配置
java·开发语言·后端·spring·rpc·java-ee
Genevieve_xiao1 分钟前
【数据结构】【xjtuse】八股文单元小测
数据结构·算法
Xの哲學11 分钟前
Linux slab分配器深度剖析:从原理到实践
linux·服务器·算法·架构·边缘计算
leonardee38 分钟前
Android和JAVA面试题相关资料
java·后端
w***4241 小时前
Spring Boot 条件注解:@ConditionalOnProperty 完全解析
java·spring boot·后端
q***97911 小时前
Spring Cloud GateWay搭建
java
明洞日记2 小时前
【设计模式手册007】原型模式 - 通过复制创建对象的艺术
java·设计模式·原型模式
普通网友2 小时前
C++中的组合模式
开发语言·c++·算法
2501_941111462 小时前
C++中的组合模式变体
开发语言·c++·算法
源码君miui520862 小时前
JAVA国际版同城服务同城信息同城任务发布平台APP源码Android + IOS
android·java·ios