从零学算法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;
  }
相关推荐
咖啡教室2 小时前
java日常开发笔记和开发问题记录
java
咖啡教室2 小时前
java练习项目记录笔记
java
鱼樱前端3 小时前
maven的基础安装和使用--mac/window版本
java·后端
RainbowSea3 小时前
6. RabbitMQ 死信队列的详细操作编写
java·消息队列·rabbitmq
RainbowSea3 小时前
5. RabbitMQ 消息队列中 Exchanges(交换机) 的详细说明
java·消息队列·rabbitmq
算AI4 小时前
人工智能+牙科:临床应用中的几个问题
人工智能·算法
李少兄5 小时前
Unirest:优雅的Java HTTP客户端库
java·开发语言·http
此木|西贝5 小时前
【设计模式】原型模式
java·设计模式·原型模式
可乐加.糖5 小时前
一篇关于Netty相关的梳理总结
java·后端·网络协议·netty·信息与通信
s9123601015 小时前
rust 同时处理多个异步任务
java·数据库·rust