【剑指Offer】个人学习笔记_41_数据流中的中位数
目录
- 题目:
- [剑指 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. 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
输入:
["MedianFinder","addNum","addNum","findMedian","addNum","findMedian"]
[[],[1],[2],[],[3],[]]
输出:[null,null,null,1.50000,null,2.00000]
输入:
["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_数据流中的中位数相关推荐
- 剑指offer——面试题64:数据流中的中位数
剑指offer--面试题64:数据流中的中位数 Solution1: 最笨的方法了... class Solution { public:void Insert(int num) {num_strea ...
- 剑指offer:面试题41. 数据流中的中位数
题目:数据流中的中位数 如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值.如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数 ...
- 剑指Offer的学习笔记(C#篇)-- 数组中重复的数字
题目描述 在一个长度为n的数组里的所有数字都在0到n-1的范围内. 数组中某些数字是重复的,但不知道有几个数字是重复的.也不知道每个数字重复几次.请找出数组中任意一个重复的数字. 例如,如果输入长度为 ...
- 剑指Offer的学习笔记(C#篇)-- 用两个栈实现队列
题目描述 用两个栈来实现一个队列,完成队列的Push和Pop操作. 队列中的元素为int类型. 一 . 概念! 首先要理解栈和队列的概念. 1. 栈:咱可以简单的把栈理解成装羽毛球的球桶.或者我们吃的 ...
- 【剑指Offer】剑指Offer刷题笔记
数组和字符串 剑指 Offer 04. 二维数组中的查找 在一个 n * m 的二维数组中,每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺序排序.请完成一个高效的函数,输入这样的一个 ...
- 剑指offer——面试题45:圆圈中最后剩下的数字(孩子们的游戏)
剑指offer--面试题45:圆圈中最后剩下的数字(孩子们的游戏) Solution1: 当年第一遍做时,自己想的垃圾算法 class Solution { public:int LastRemain ...
- 【LeetCode】剑指 Offer 43. 1~n 整数中 1 出现的次数
[LeetCode]剑指 Offer 43. 1-n 整数中 1 出现的次数 文章目录 [LeetCode]剑指 Offer 43. 1-n 整数中 1 出现的次数 package offer;pub ...
- 【LeetCode】剑指 Offer 53 - I. 在排序数组中查找数字 I
[LeetCode]剑指 Offer 53 - I. 在排序数组中查找数字 I 文章目录 [LeetCode]剑指 Offer 53 - I. 在排序数组中查找数字 I 一.二分法 总结 一.二分法 ...
- 【算法】剑指 Offer 53 - II. 0~n-1中缺失的数字
1.概述 剑指 Offer 53 - II. 0-n-1中缺失的数字 一个长度为n-1的递增排序数组中的所有数字都是唯一的,并且每个数字都在范围0-n-1之内.在范围0-n-1内的n个数字中有且只有一 ...
最新文章
- 关于使用JAVA中JDK安装和在命令行中编译和运行程序的一些总结
- innerHTML的确是好东西
- 北京沙龙报名 | 关于Dubbo开源的那些事儿
- CCNA-数据包在网络设备直接的通信流程
- C++之构造函数和析构函数强化
- C语言 数组遍历 - C语言零基础入门教程
- LETTERS (信息学奥赛一本通-T1212)
- GAN之再进化:分布判别器,大连理工提出一种新式无监督图像合成方法
- SQLite Tutorial 4 : How to export SQLite file into CSV or Excel file
- 大快搜索获评“2018中国大数据基础软件领域领军企业”
- 软件测试佣金问题等价类,测试用例设计白皮书--等价类划分方法
- win11系统完全使用ie浏览器的方法
- 统计学的Python实现-014:几何平均数
- 助睡眠的神器,这些东西能改善失眠,一觉睡到天亮
- 嘉兴 机器人仓库 菜鸟_菜鸟物流嘉兴未来园区的工业机器人系统运维员的一天...
- 锚文本链接用html怎么做,锚文本链接是什么?
- python画长方形的代码_python使用turtle画一个三角形、正方形(矩形或四边形)
- arcgis操作导入点线表
- python抓取酷我MV
- CSMA/CD 协议 详解