LeetCode295之数据流的中位数(相关话题:优先队列)
创始人
2024-05-30 12:47:34
0

题目描述

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

  • 例如 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

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNum 和 findMedian

思路分析

一开始没看懂题目,以为只要用List存储数据,取中位数即可,认真审题可以发现,中位数是有序整数列表中的中间值,所以必须对插入的数据先排序才能求中位值

在数据流中,数据会不断涌入结构中,那么也就面临着需要多次动态调整以获得中位数。 因此实现的数据结构需要既需要快速找到中位数,也需要做到快速调整。

首先能想到就是二叉搜索树,在平衡状态下,树顶必定是中间数,然后再根据长度的奇偶性决定是否取两个数。

此方法效率高,但是手动编写较费时费力。

根据只需获得中间数的想法,可以将数据分为左右两边,一边以最大堆的形式实现,可以快速获得左侧最大数, 另一边则以最小堆的形式实现。其中需要注意的一点就是左右侧数据的长度差不能超过1。 这种实现方式的效率与AVL平衡二叉搜索树的效率相近,但编写更快

显然,为了可以在 O(1) 的复杂度内取得当前中位数,我们应当令 l 为大根堆,r 为小根堆,并人为固定 l 和 r 之前存在如下的大小关系:

  1. 当数据流元素数量为偶数:l 和 r 大小相同,此时动态中位数为两者堆顶元素的平均值;
  2. 当数据流元素数量为奇数:l 比 r 多一,此时动态中位数为 l 的堆顶原数。

为了满足上述说的奇偶性堆大小关系,在进行 addNum 时,我们应当分情况处理:

插入前两者大小相同,说明插入前数据流元素个数为偶数,插入后变为奇数。我们期望操作完达到「l 的数量为 r 多一,同时双堆维持有序」,进一步分情况讨论:

  • 如果 r 为空,说明当前插入的是首个元素,直接添加到 l 即可;
  • 如果 r 不为空,且 num <= r.peek(),说明 num 的插入位置不会在后半部分(不会在 r 中),直接加到 l 即可;
  • 如果 r 不为空,且 num > r.peek(),说明 num 的插入位置在后半部分,此时将 r 的堆顶元素放到 l 中,再把 num 放到 r(相当于从 r 中置换一位出来放到 l 中)。

插入前两者大小不同,说明前数据流元素个数为奇数,插入后变为偶数。我们期望操作完达到「l 和 r 数量相等,同时双堆维持有序」,进一步分情况讨论(此时 l 必然比 r 元素多一):

  • 如果 num >= l.peek(),说明 num 的插入位置不会在前半部分(不会在 l 中),直接添加到 r 即可。
  • 如果 num < l.peek(),说明 num 的插入位置在前半部分,此时将 l 的堆顶元素放到 r 中,再把 num 放入 l 中(相等于从 l 中替换一位出来当到 r 中)。

 代码实现

class MedianFinder {//大顶堆PriorityQueue l = new PriorityQueue<>((a,b)->b-a);//小顶堆(默认)PriorityQueue r = new PriorityQueue<>((a,b)->a-b);public void addNum(int num) {int s1 = l.size(), s2 = r.size();if (s1 == s2) {if (r.isEmpty() || num <= r.peek()) {l.add(num);} else {l.add(r.poll());r.add(num);}} else {if (l.peek() <= num) {r.add(num);} else {r.add(l.poll());l.add(num);}}}public double findMedian() {int s1 = l.size(), s2 = r.size();if (s1 == s2) {return (l.peek() + r.peek()) / 2.0;} else {return l.peek();}}
}

相关内容

热门资讯

小仓鼠在笼子里一动不动的是怎么... 小仓鼠在笼子里一动不动的是怎么回事?小仓鼠应该是生病了,建议尽快处理,找兽医看看,他们最专业。目前应...
扬州何园的寻访感受 扬州何园的寻访感受 你好,请参考: 初见何园,似乎有种似曾相识的感觉,或许意识到这种想...
童年傻事作文---速来 童年傻事作文---速来童年傻事 童年是多么美好,童年的生活令人向往,童年的天空总是那么蓝,而童...
家庭幽默大赛老韩头一家幽默服装... 家庭幽默大赛老韩头一家幽默服装秀家庭幽默大赛老韩头一家幽默服装秀这都被他看出来了  今天晒内裤了,室...
乐器名称 乐器名称我在小提琴协奏曲《梁祝》的视频里这种乐器不知叫什么名字,请懂音乐和乐器的朋友给予解答,谢谢。...
电视剧《老马家的幸福往事》中的... 电视剧《老马家的幸福往事》中的马鸣和徐丽娜在马鸣的大学里的图书馆里偷书时用留声机听的歌是什么名字的请...
流鬼的介绍 流鬼的介绍 流鬼,是古代民族,分布在今俄罗斯勘察加半岛,有数万人。
请问为爱所困火吗 请问为爱所困火吗一般般。爱情偶像剧。此剧上一年开播,还上了微博热搜榜我认为很火,为爱所困第二部还有不...
什么是手诊 什么是手诊手诊的概念就是指通过人体手的纹路形态、变化、规律等方式,对人体器官的演变作出推理的一种防治...
双子星公主法和希的有几集,第一... 双子星公主法和希的有几集,第一部第二部都要,如果知道第三部也透露一点,请求不要编,法和希最后怎样了拜...
王熙凤简介 王熙凤简介《红楼梦》中人物,贾琏之妻,王夫人的内侄女。长着一双丹凤三角眼,两弯柳叶吊梢眉,身量苗条,...
大学生应从哪些方面进行自我探索 大学生应从哪些方面进行自我探索兴趣、能力、价值观、性格,这四个是最主要的方面,其中价值观是核心
谁有好看的卡通人物的电脑背景! 谁有好看的卡通人物的电脑背景!卡通人物的背景要清色! 看得清 要男生的 我有Clannad主题...
幼儿园小班孩子座位固定好还是经... 幼儿园小班孩子座位固定好还是经常换好?我家孩子座位老换,我觉得不好,大家觉得呢?... 我家孩子座...
我的爸爸的作文 我的爸爸的作文我的父亲 人们常说父爱如山,可在我看来,我的父亲对我的爱,并非完全如山那样严峻,有时却...
潘朵拉之心第二季动漫什么时候出 潘朵拉之心第二季动漫什么时候出动画只出了第一季 没有要出第二季的消息 可能性估计很小目前漫画还在连载...
时不我待是什么意思 时不我待是什么意思时不我待的意思是时间不等待人,要抓紧时间,不要虚度光阴。出处:日月逝矣,岁不我与。...
葫芦小金刚里大娃怎么被抓的 葫芦小金刚里大娃怎么被抓的掉泥潭被捉的
有一部小说叫穿越千年来爱你还是... 有一部小说叫穿越千年来爱你还是什么的有一部小说叫穿越千年来爱你还是什么的内容讲的是一个女的在路上走着...
火影忍者力 米娜是红眼吗 火影忍者力 米娜是红眼吗红眼?首先这几集是原创,非岸本创作。在岸本的剧情结构里面只有木叶白眼、写轮眼...