目录

  • 题目:
    • [剑指 Offer 41. 数据流中的中位数](https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/)
    • 题目分析
  • 初始解答:
  • 学习他人:
    • 方法一:
    • 方法二:
    • 方法三:
  • 总结

刷题日期:下午7:49 2021年5月7日星期五

个人刷题记录,代码收集,来源皆为leetcode

经过多方讨论和请教,现在打算往Java方向发力

主要答题语言为Java

题目:

剑指 Offer 41. 数据流中的中位数

难度困难141

如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。

例如,

[2,3,4] 的中位数是 3

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

设计一个支持以下两种操作的数据结构:

示例 1:

输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]

示例 2:

输入:
["MedianFinder","addNum","findMedian","addNum","findMedian"]
[[],[2],[],[3],[]]
输出:[null,null,2.00000,null,2.50000]

限制:

题目分析

基本没什么头绪,在返回中位数的过程中会遇到排序,然后判断。

初始解答:

困难题目,尝试构思一下思路

class MedianFinder {/** initialize your data structure here. */public MedianFinder() {//这里需要考虑用什么数据结构来实现数据流//需要同时满足排序和插入的快捷,参考书本考虑最大堆来实现}public void addNum(int num) {//分为前后两个堆,由于本身有序,插入的位置也容易找到}public double findMedian() {if (obj.length % 2 == 1) return obj[obj.length>>1]//为奇数的情况if (obj.length % 2 == 0) return 0.5 * (obj[(obj.length>>1)-1]+obj[obj.length>>1])//为偶数的情况}
}/*** Your MedianFinder object will be instantiated and called as such:* MedianFinder obj = new MedianFinder();* obj.addNum(num);* double param_2 = obj.findMedian();*/

看了书发现这题考的还有诸多数据存储类型的特点。看过评论区的解答长度就明白这题为啥是困难了。

学习方法三

class MedianFinder {Queue<Integer> A, B; //两个堆,还是队列?和流比较接近/** initialize your data structure here. */public MedianFinder() {//这里需要考虑用什么数据结构来实现数据流//需要同时满足排序和插入的快捷,参考书本考虑最大堆来实现A = new PriorityQueue<>(); //小顶堆,元素为较大那一部分//这操作太骚了B = new PriorityQueue<>((x, y) -> (y - x)); //大顶堆,元素为较小那一部分}public void addNum(int num) {//分为前后两个堆,由于本身有序,插入的位置也容易找到if(A.size() != B.size()) {A.add(num);  //如果前后不等,A先入队B.add(A.poll()); //B接收A刚出队的元素,保持两堆一致} else { //否则相反,如果两堆元素一样多,则加给后面B.add(num);A.add(B.poll());}}public double findMedian() {//如果两个一样多,则返回均值,否则返回小顶堆的堆顶return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;}
}/*** Your MedianFinder object will be instantiated and called as such:* MedianFinder obj = new MedianFinder();* obj.addNum(num);* double param_2 = obj.findMedian();*/

执行结果:通过

显示详情 添加备注

执行用时:85 ms, 在所有 Java 提交中击败了54.04%的用户

内存消耗:49.5 MB, 在所有 Java 提交中击败了76.21%的用户

学习他人:

方法一:

无力明明

(编辑过)2020-03-11

用大顶堆+小顶堆方法,可以看作大顶堆是普通班,小顶堆是实验班。数量上时刻保持 小顶-大顶<=1(两堆相等或者小顶比大顶多一个)。

新学生先入普通班(大顶堆),此时可能会失去平衡了,于是取大顶堆的第一个(班里最好的学生)加入实验班(小顶堆),判断若数量过多(不是等于或多一个),取第一个(实验班里最差的学生)到普通班(大顶堆)里。 取中位数的时候,若两堆数量相等,则各取堆顶取平均,若小顶比大顶多一,则多的那一个就是中位数。

--------++++++++±-------

class MedianFinder {PriorityQueue<Integer> left;//大顶PriorityQueue<Integer> right;//小顶public MedianFinder() {left=new PriorityQueue<>((n1,n2)->n2-n1);right=new PriorityQueue<>();}public void addNum(int num) {left.add(num);right.add(left.poll());if(left.size()+1<right.size())left.add(right.poll());}public double findMedian() {if(right.size()>left.size())return right.peek();return (double)(left.peek()+right.peek())/2;}
}

方法二:

ZML400

(编辑过)2020-03-14

维持一个大顶堆和小顶堆,确保:

1、大小顶堆元素数量差小于等于1

2、大顶堆中所有元素均小于小顶堆中元素

返回结果:

大小顶堆元素数量相等时,返回两个堆顶的平均值,否则返回较长堆的堆顶。

执行用时 :73 ms, 在所有 Java 提交中击败了94.29%的用户
内存消耗 :55.4 MB, 在所有 Java 提交中击败了100.00%的用户class MedianFinder {/** initialize your data structure here. */List<Integer> maxHeap,minHeap;public MedianFinder() {maxHeap = new ArrayList<>();minHeap = new ArrayList<>();}   public void addNum(int num) {if(maxHeap.size()==0){maxHeap.add(num);return;}if(num>maxHeap.get(0)){minHeap.add(num);DownToUp(minHeap,0);}else{maxHeap.add(num);DownToUp(maxHeap,1);}//保持maxHeap和MinHeap长度之差小于等于1while(Math.abs(maxHeap.size()-minHeap.size())>1){if(maxHeap.size()>minHeap.size()){minHeap.add(maxHeap.get(0));DownToUp(minHeap,0);maxHeap.set(0,maxHeap.get(maxHeap.size()-1));maxHeap.remove(maxHeap.size()-1);upToDown(maxHeap,1);}else{maxHeap.add(minHeap.get(0));DownToUp(maxHeap,1);minHeap.set(0,minHeap.get(minHeap.size()-1));minHeap.remove(minHeap.size()-1);upToDown(minHeap,0);}}}public double findMedian() {if(maxHeap.size()==minHeap.size())return (maxHeap.get(0)+minHeap.get(0))/2.0;if(maxHeap.size()>minHeap.size())return maxHeap.get(0);return minHeap.get(0);}public void DownToUp(List<Integer> list,int flag){ //插入时向上调整//flag = 0表示调整小顶堆,flag=1表示调整大顶堆if(list.size()<=1)return;int temp = list.get(list.size()-1);int i =list.size()-1,j;while(i!=0){j=(i-1)/2;if(flag == 0){//小顶堆if(temp>=list.get(j))break;list.set(i,list.get(j));i=j;}else{ //大顶堆if(temp<=list.get(j))break;list.set(i,list.get(j));i=j;}}list.set(i,temp);}public void upToDown(List<Integer> list,int flag){//删除时向下调整//flag = 0表示调整小顶堆,flag=1表示调整大顶堆if(list.size()<=1)return;int temp = list.get(0);int i =0,j,n=list.size()-1;while(i!=list.size()-1){j=i*2+1;if(j>n)break;if(flag == 0){//小顶堆if(j<n&&list.get(j)>list.get(j+1))j++;if(temp<=list.get(j))break;list.set(i,list.get(j));i=j;}else{ //大顶堆if(j<n&&list.get(j)<list.get(j+1))j++;if(temp>=list.get(j))break;list.set(i,list.get(j));i=j;}}list.set(i,temp);}
}

方法三:

K神,堆实现复杂度分析

时间复杂度:
查找中位数 O(1)O(1) : 获取堆顶元素使用 O(1)O(1) 时间;
添加数字 O(\log N)O(logN) : 堆的插入和弹出操作使用 O(\log N)O(logN) 时间。
空间复杂度 O(N)O(N) : 其中 NN 为数据流中的元素数量,小顶堆 AA 和大顶堆 BB 最多同时保存 NN 个元素。

作者:jyd
链接:https://leetcode-cn.com/problems/shu-ju-liu-zhong-de-zhong-wei-shu-lcof/solution/mian-shi-ti-41-shu-ju-liu-zhong-de-zhong-wei-shu-y/
来源:力扣(LeetCode)

Java 使用 PriorityQueue<>((x, y) -> (y - x)) 可方便实现大顶堆。

class MedianFinder {Queue<Integer> A, B;public MedianFinder() {A = new PriorityQueue<>(); // 小顶堆,保存较大的一半B = new PriorityQueue<>((x, y) -> (y - x)); // 大顶堆,保存较小的一半}public void addNum(int num) {if(A.size() != B.size()) {A.add(num);B.add(A.poll());} else {B.add(num);A.add(B.poll());}}public double findMedian() {return A.size() != B.size() ? A.peek() : (A.peek() + B.peek()) / 2.0;}
}

总结

实现题目要求不同数据存储结构的时间复杂度:

数据结构 插入的时间复杂度 得到中位数的时间复杂度
未排序数组 O(1) O(n)
排序数组 O(n) O(1)
排序链表 O(n) O(1)
二叉搜索树 平均O(logn)最差O(n) 平均O(logn)最差O(n)
AVL树 O(logn) O(1)
最大堆和最小堆 O(logn) O(1)

AVL树的时间效率很高,但大部分编程语言的函数库中都没有实现这个数据结构,所以考虑堆实现。

以上就是本题的内容和学习过程了,分析得出堆的过程本身就很不容易了,考验基本功,不愧是困难。

欢迎讨论,共同进步。

【剑指Offer】个人学习笔记_41_数据流中的中位数相关推荐

  1. 剑指offer——面试题64:数据流中的中位数

    剑指offer--面试题64:数据流中的中位数 Solution1: 最笨的方法了... class Solution { public:void Insert(int num) {num_strea ...

  2. 剑指offer:面试题41. 数据流中的中位数

    题目:数据流中的中位数 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数 ...

  3. 剑指Offer的学习笔记(C#篇)-- 数组中重复的数字

    题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...

  4. 剑指Offer的学习笔记(C#篇)-- 用两个栈实现队列

    题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 一 . 概念! 首先要理解栈和队列的概念. 1. 栈:咱可以简单的把栈理解成装羽毛球的球桶.或者我们吃的 ...

  5. 【剑指Offer】剑指Offer刷题笔记

    数组和字符串 剑指 Offer 04. 二维数组中的查找 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个高效的函数,输入这样的一个 ...

  6. 剑指offer——面试题45:圆圈中最后剩下的数字(孩子们的游戏)

    剑指offer--面试题45:圆圈中最后剩下的数字(孩子们的游戏) Solution1: 当年第一遍做时,自己想的垃圾算法 class Solution { public:int LastRemain ...

  7. 【LeetCode】剑指 Offer 43. 1~n 整数中 1 出现的次数

    [LeetCode]剑指 Offer 43. 1-n 整数中 1 出现的次数 文章目录 [LeetCode]剑指 Offer 43. 1-n 整数中 1 出现的次数 package offer;pub ...

  8. 【LeetCode】剑指 Offer 53 - I. 在排序数组中查找数字 I

    [LeetCode]剑指 Offer 53 - I. 在排序数组中查找数字 I 文章目录 [LeetCode]剑指 Offer 53 - I. 在排序数组中查找数字 I 一.二分法 总结 一.二分法 ...

  9. 【算法】剑指 Offer 53 - II. 0~n-1中缺失的数字

    1.概述 剑指 Offer 53 - II. 0-n-1中缺失的数字 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0-n-1之内.在范围0-n-1内的n个数字中有且只有一 ...

最新文章

  1. 关于使用JAVA中JDK安装和在命令行中编译和运行程序的一些总结
  2. innerHTML的确是好东西
  3. 北京沙龙报名 | 关于Dubbo开源的那些事儿
  4. CCNA-数据包在网络设备直接的通信流程
  5. C++之构造函数和析构函数强化
  6. C语言 数组遍历 - C语言零基础入门教程
  7. LETTERS (信息学奥赛一本通-T1212)
  8. GAN之再进化:分布判别器,大连理工提出一种新式无监督图像合成方法
  9. SQLite Tutorial 4 : How to export SQLite file into CSV or Excel file
  10. 大快搜索获评“2018中国大数据基础软件领域领军企业”
  11. 软件测试佣金问题等价类,测试用例设计白皮书--等价类划分方法
  12. win11系统完全使用ie浏览器的方法
  13. 统计学的Python实现-014:几何平均数
  14. 助睡眠的神器,这些东西能改善失眠,一觉睡到天亮
  15. 嘉兴 机器人仓库 菜鸟_菜鸟物流嘉兴未来园区的工业机器人系统运维员的一天...
  16. 锚文本链接用html怎么做,锚文本链接是什么?
  17. python画长方形的代码_python使用turtle画一个三角形、正方形(矩形或四边形)
  18. arcgis操作导入点线表
  19. python抓取酷我MV
  20. CSMA/CD 协议 详解

热门文章

  1. 地形系统shader学习
  2. DeepFM:深度学习算法助力华为应用市场APP推荐
  3. REVIT 卸载工具,完美彻底卸载清除干净revit各种残留注册表和文件
  4. 2017安卓开发工程师面试题总结
  5. OCI--学习OCI编程
  6. U盘杀毒后文件不见/找回
  7. 不用下载软件,免费将PDF文件缩小的方法分享
  8. 80老翁谈人生(187):老翁为北大排名而自豪
  9. 高斯-约当 (Gauss-Jordan) 消元法 [学习笔记]
  10. C++关于string类的模拟实现