从零学算法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;
  }
相关推荐
im_AMBER2 小时前
Leetcode 74 K 和数对的最大数目
数据结构·笔记·学习·算法·leetcode
无敌最俊朗@3 小时前
STL-vector面试剖析(面试复习4)
java·面试·职场和发展
t198751283 小时前
电力系统经典节点系统潮流计算MATLAB实现
人工智能·算法·matlab
断剑zou天涯3 小时前
【算法笔记】蓄水池算法
笔记·算法
PPPPickup3 小时前
easychat项目复盘---获取联系人列表,联系人详细,删除拉黑联系人
java·前端·javascript
LiamTuc3 小时前
Java构造函数
java·开发语言
长安er3 小时前
LeetCode 206/92/25 链表翻转问题-“盒子-标签-纸条模型”
java·数据结构·算法·leetcode·链表·链表翻转
菜鸟plus+3 小时前
N+1查询
java·服务器·数据库
唯道行3 小时前
计算机图形学·23 Weiler-Athenton多边形裁剪算法
算法·计算机视觉·几何学·计算机图形学·opengl
我要添砖java4 小时前
《JAVAEE》网络编程-什么是网络?
java·网络·java-ee