1.题目要求

中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。

例如,

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

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

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

  • void addNum(int num) - 从数据流中添加一个整数到数据结构中。
  • double findMedian() - 返回目前所有元素的中位数。

示例:

  addNum(1)
addNum(2)
findMedian() -> 1.5
addNum(3)
findMedian() -> 2

进阶:

    1. 如果数据流中所有整数都在 0 到 100 范围内,你将如何优化你的算法?
    2. 如果数据流中 99% 的整数都在 0 到 100 范围内,你将如何优化你的算法?

2.解题思路

堆是一个非常重要的数据结构,堆排序在C++中的实现为优先级队列(Priority_queue),关于这一点,我的另一篇博文 "Leetcode 703. 数据流中的第K大元素"  有更详细提到,这里不做重复。

LeetCode网站把这一道划分在“堆”一类中,也是提醒我们使用堆结构。这道题很巧妙,我是听了算法课(牛客网的左程云大牛)的讲解才弄明白。这里的代码是自己听懂了思路,独立写出来的。

关键思路:建立两个堆(使用priority_queue实现),一个大根堆,一个小根堆。

(1)一个大根堆,保存所有整数中较小的1/2;一个小根堆,保存所有整数中较大的1/2;
          (2)并且,依次添加元素过程中,两个堆元素个数的差的绝对值不能超过1;

这样,两个堆建立好了以后,

(1)如果输入的元素个数 n 是偶数,则两个堆的元素个数相等,分别取大根堆的顶和小根堆的顶,取平均值,即是所求的整个数据流的中位数;

(2)如果输入的元素个数 n 是奇数,则必有一个堆的元素个数为(n/2+1),返回这个堆的顶,即为所求的中位数。

3.我的代码

个人比较喜欢写段落注释和行注释,因为这样自己一年之后还能快速看懂,当然也方便他人,特别是一起刷题的伙伴,轻松看懂。

更多的细节讲解里都在注释里。如有错误的地方,欢迎多指正。

代码通过所有测试案例的时间为124ms。

class MedianFinder {
public:
/** initialize your data structure here. */
MedianFinder() { } void addNum(int num) {
/*建立两个堆:(1)一个大根堆,保存所有整数中较小的1/2;一个小根堆,保存所有整数中较大的1/2;
(2)并且,依次添加元素过程中,两个堆大小的差的绝对值不能超过1; */ //第一元素加入大根堆
if(heap1.size()==){
heap1.push(num);
return;
} if(num<=heap1.top()){
//第二个元素比大根堆的顶小
heap1.push(num); //大根堆元素过多
if(heap1.size()-heap2.size()>)
{
int temp = heap1.top();
heap1.pop();
heap2.push(temp);//大根堆弹出顶到小根堆
} }
else{
//第二个元素比大根堆的顶大,直接进入小根堆
heap2.push(num); //小根堆元素过多
if(heap2.size()-heap1.size()>)
{
int temp = heap2.top();
heap2.pop();
heap1.push(temp);//小根堆弹出顶到大根堆
}
} } double findMedian() {
//输入的元素为奇数个
if(heap1.size() > heap2.size())
return heap1.top();
else if(heap1.size() < heap2.size())
return heap2.top(); //输入的元素个数为偶数
else
return (heap1.top()+heap2.top())/2.0;
//取大根堆、小根堆的堆顶元素取平均值,即为所求全局中位数
} private:
priority_queue<int> heap1;//默认,大根堆
priority_queue<int,vector<int>,greater<int>> heap2;//小根堆(升序序列) }; /**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/

4.用时更少的示例代码

这是我提交解答后,查看细节,看到的Leetcode官网上提交的关于这道题运行时间最短(96ms)的示例代码。

LeetCode上刷好多速度排名第一的代码中都有一段类似的代码,就是下面代码中的第一段代码——优化C++的IO速度。

/*一般地,C++的运行速度不如C的,主要原因是C++的输入输出流兼容了C的输入输出,因此,C++的速度才会变慢,
如果去掉C++的输入输出的兼容性的话,速度就和C的差不多了*/
static const auto __ = []() {
// turn off sync
std::ios::sync_with_stdio(false);
// untie in/out streams
std::cin.tie(nullptr);
return nullptr;
}(); class MedianFinder {
public:
/** initialize your data structure here. */ //使用vector实现两个堆,而不是priority_queue
vector<int> maxheap;
vector<int> minheap; bool flag = true; MedianFinder() {
} void addNum(int num) {
if(flag){
//构建小根堆
if(minheap.size()>&&num>minheap[]){
minheap.push_back(num);
push_heap(minheap.begin(),minheap.end(),greater<int>());
num = minheap[];
pop_heap(minheap.begin(),minheap.end(),greater<int>());
minheap.pop_back();
}
maxheap.push_back(num);
push_heap(maxheap.begin(),maxheap.end(),less<int>());
flag=false;
}else{
//构建大根堆
if(maxheap.size()>&&num<maxheap[]){
maxheap.push_back(num);
push_heap(maxheap.begin(),maxheap.end(),less<int>());
num = maxheap[];
pop_heap(maxheap.begin(),maxheap.end(),less<int>());
maxheap.pop_back();
}
minheap.push_back(num);
push_heap(minheap.begin(),minheap.end(),greater<int>());
flag=true;
}
} double findMedian() {
if(maxheap.size()<&&minheap.size()<)
return ;
if(flag){
return (maxheap[]+minheap[])/2.0;
}else{
return maxheap[];
}
}
}; /**
* Your MedianFinder object will be instantiated and called as such:
* MedianFinder obj = new MedianFinder();
* obj.addNum(num);
* double param_2 = obj.findMedian();
*/

参考博客:

https://blog.csdn.net/xiaosshhaa/article/details/78136032   std::ios::sync_with_stdio(false); cin.tie(0);

Leetcode 295. 数据流的中位数的更多相关文章

  1. Java实现 LeetCode 295 数据流的中位数

    295. 数据流的中位数 中位数是有序列表中间的数.如果列表长度是偶数,中位数则是中间两个数的平均值. 例如, [2,3,4] 的中位数是 3 [2,3] 的中位数是 (2 + 3) / 2 = 2. ...

  2. [LeetCode] 295. Find Median from Data Stream 找出数据流的中位数

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  3. LeetCode——295. Find Median from Data Stream

    一.题目链接: https://leetcode.com/problems/find-median-from-data-stream 二.题目大意: 给定一段数据流,要求求出数据流中的中位数,其中数据 ...

  4. 堆实战(动态数据流求top k大元素,动态数据流求中位数)

    动态数据集合中求top k大元素 第1大,第2大 ...第k大 k是这群体里最小的 所以要建立个小顶堆 只需要维护一个大小为k的小顶堆 即可 当来的元素(newCome)> 堆顶元素(small ...

  5. [LeetCode] 295. Find Median from Data Stream ☆☆☆☆☆(数据流中获取中位数)

    295. Find Median from Data Stream&数据流中的中位数 295. Find Median from Data Stream https://leetcode.co ...

  6. [leetcode]295. Find Median from Data Stream数据流的中位数

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  7. [LeetCode] Find Median from Data Stream 找出数据流的中位数

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

  8. 295 Find Median from Data Stream 数据流的中位数

    中位数是排序后列表的中间值.如果列表的大小是偶数,则没有中间值,此时中位数是中间两个数的平均值.示例:[2,3,4] , 中位数是 3[2,3], 中位数是 (2 + 3) / 2 = 2.5设计一个 ...

  9. [Swift]LeetCode295. 数据流的中位数 | Find Median from Data Stream

    Median is the middle value in an ordered integer list. If the size of the list is even, there is no ...

随机推荐

  1. gitignore 文件生效办法

    .gitignore 可以添加一些不加入git版本控制的文件 比如一些测试文件.因人而异的配置信息等等 .gitignore 文件展示如下 /.idea/target//.classpath /.pr ...

  2. C++ ifndef /define/ endif 作用和用法

    ifndef/define/endif”主要目的是防止头文件的重复包含和编译 比如你有两个C文件,这两个C文件都include了同一个头文件.而编译时,这两个C文件要一同编译成一个可运行文件,于是问题 ...

  3. python SyntaxError: Non-ASCII character '\xe8' in file C:\Users\nwpujun\PycharmProjects\projects\hrl1\hrlAgent\src\li_nn.py on line 52

    解决方法:在文件头部加上这样的一句话 # -*- coding: utf-8 -*- 注意:加在其他的位置可能没用,我就是这样的

  4. 冥冥中转到了mac 上进行开发

    2013年愚人节前我的开发环境情况 我是一个有着15年windows使用经历的老programer,如果算上dos那还可以加两年.当过小企业网管,做过十二年的开发工作(直到老死,~_~).这期间当然也 ...

  5. POJ 2631 Roads in the North(求树的直径,两次遍历 or 树DP)

    题目链接:http://poj.org/problem?id=2631 Description Building and maintaining roads among communities in ...

  6. POJ 3498 March of the Penguins(网络最大流)

    Description Somewhere near the south pole, a number of penguins are standing on a number of ice floe ...

  7. Java学习个人备忘录之文档注释

    文档注释 单行注释用 // 多行注释有两种,第一种是 /* 内容 */,第二种是/** 内容 */. 这两种多行注释的区别是/** 内容 */这种注释可以生成一个该文件的注释文档,下面是演示代码. A ...

  8. Android 开发 之 JNI入门 - NDK从入门到精通

    NDK项目源码地址 : -- 第一个JNI示例程序下载 : GitHub - https://github.com/han1202012/NDKHelloworld.git -- Java传递参数给C ...

  9. TCP系列16—重传—6、基础快速重传(Fast Retransmit)

    一.快速重传介绍 按照TCP协议,RTO超时重传是一个非常重要的事件,当RTO超时的时候,TCP会同时通过两种方式非常谨慎的降低发送数据包的速率,一种是基于拥塞控制削减发送窗口的大小,另外一个是通过指 ...

  10. 【Docker 教程】- Docker 架构

    1.Docker 使用客户端-服务器 (C/S) 架构模式,使用远程API来管理和创建Docker容器. 2.Docker 容器通过 Docker 镜像来创建. 3.容器与镜像的关系类似于面向对象编程 ...