前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >Day7-线性表-堆-寻找中位数

Day7-线性表-堆-寻找中位数

作者头像
BUPTrenyi
发布于 2019-07-16 03:06:10
发布于 2019-07-16 03:06:10
37700
代码可运行
举报
运行总次数:0
代码可运行

一 唠唠嗑

咳咳,今天是第七天了,来讲一道利用堆的,很巧,但是很简单的题

什么?你问我为什么拖更?

二 来吧!

Q:设计一个数据结构,该数据结构动态维护一组数据,且支持以下操作:

1.添加元素:void addNum(int num),将num添加进数据结构中

2.返回中位数:double findMedian(),返回其维护的中位数

中位数定义:

若数据个数为奇数,中位数是该数据排序后中间的数。

【1,2,3】,return 2

若数据个数为偶数,中位数是该数据排序后中间两个数的平均值。

【1,2,3,4】,return 2.5

三 想想呗

此时你脱口而出,这还不简单?找个数组存啊,插入一个就排序啊,排完序之后,根据数组个数找中位数呗,这种题也拿来面我,渣渣

此时的面试官狡黠一笑,哦,看起来可以,那你能不能给我一个时间复杂度为N的算法?

你:

然后面试官

好了,说正经的,显然不能这么干啊。

最好的算法是:维护一个最大堆,和最小堆

时刻保持最大堆堆顶,一直小于最小堆堆顶

且时刻保持,最大堆,与最小堆的元素个数差,<=1

这样就保持了,最大堆里存一半相对小的数,最小堆里存了一半相对大的数

取中位数的时候,根据最大堆,最小堆元素个数来判断,是取谁的堆顶元素

这样我就做到了,时间复杂度为N,即,插完,同时排完,同时取中位数为O(1)

听起来有点拗口?没关系,举个栗子就好了~

对于数组nums = 【6,10,1,7,99,4,33】

遍历进行插入我维护的“数据结构”

第一个元素,直接插最大堆

插入后最大堆【6】,最小堆【】

第二个元素,此时最大堆size(是1)> 最小堆size(是0)

即第二种情况:此时10 > 最大堆堆顶元素6,直接将10插入最小堆

插入后最大堆【6】,最小堆【10】

第三个元素,此时最大堆size(是1)= 最小堆size(是1)

即第一种情况:此时1 < 最大堆堆顶元素6,就插入最大堆

插入后最大堆【6,1】,最小堆【10】

第四个元素,此时最大堆size(是2)> 最小堆size(是1)

仍然是第二种情况:此时7 > 最大堆堆顶元素6,直接将7插入最小堆

插入后最大堆【6,1】,最小堆【7,10】

第五个元素,此时最大堆size(是2)= 最小堆size(是2)

仍然是第一种情况:此时99 > 最大堆堆顶元素6,就插入最小堆

插入后最大堆【6,1】,最小堆【7,10,99】

第六个元素,此时最大堆size(是2)< 最小堆size(是3)

即第三种情况:此时4 < 最小堆堆顶元素7,直接插入最大堆

插入后最大堆【6,1,4】,最小堆【7,10,99】

同理第七个元素,插入到最小堆。

求中位数时,逻辑简单,注释中就写的非常清晰了

需要注意的是,第二三种情况下,有逻辑,栗子中没覆盖到,大家还是动动手,体会一下为什么这样做

四 完整代码及详细注释

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
//
// Created by renyi on 2019-06-10.
//
#include <iostream>
#include <queue>
#include <vector>
using namespace std;

class FindMedian{
public:
    FindMedian(){//核心思想:每次插入后,最大堆堆顶,必须要小于最小堆堆顶,且插入前后,最大堆,最小堆元素个数差,要<=1
    }
    void addNum(int num){//因为最大堆元素整体小于最小堆,所以进来的第一个元素先插入最大堆
        if (more_heap.empty()){//若最大堆元素是空,即这是插第一个元素
            more_heap.push(num);//插入最大堆,然后返回,再插就走下面逻辑了
            return;
        }

        if (more_heap.size() == less_heap.size()){//当最大堆,最小堆元素个数一样时
            if (num < more_heap.top()){//当num小于最大堆堆顶时,即第一种情况
                more_heap.push(num);//直接插入最大堆
            } else{
                less_heap.push(num);//直接插入最小堆
            }
        }

        else if (more_heap.size() > less_heap.size()){//最大堆元素个数,大于最小堆元素个数
            if (num > more_heap.top()){//对应第二种情况
                less_heap.push(num);
            } else{
                less_heap.push(more_heap.top());
                more_heap.pop();
                more_heap.push(num);
            }
        }

        else if (more_heap.size() < less_heap.size()){//对应第三种情况
            if (num < less_heap.top()){
                more_heap.push(num);
            } else{
                more_heap.push(less_heap.top());
                less_heap.pop();
                less_heap.push(num);
            }
        }
    }

    double findMedian(){
        if (more_heap.size() == less_heap.size()){//当元素个数相同时,总数为偶数,中位数为中间两个数的平均数
            return (more_heap.top() + less_heap.top()) / 2;
        } else if (more_heap.size() > less_heap.size()){
            return more_heap.top();//当两者元素个数不一样,必定是奇+偶,是奇
        } else{//总数奇数个,取中间的即可
            return less_heap.top();//那么最大堆,最小堆,谁元素更多,谁的堆顶就是中间的数了
        }
    }

private:
    priority_queue<double> more_heap;//初始化一个最大堆
    priority_queue<double, vector<double>, greater<double> > less_heap;//初始化一个最小堆
};

int main(){
    FindMedian findMedian;
    int nums[] = {6, 10, 1, 7, 99, 4, 33};
    for (int i = 0; i < 7; ++i) {
        findMedian.addNum(nums[i]);
        printf("此时的中位数为:%.1f\n", findMedian.findMedian());
    }
    return 0;
}

运行结果

五 总结

好记性不如烂笔头,赶紧的动手实现吧

什么?你问我堆是什么?

那就请移步作者上一篇文章好了

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2019-06-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 算法其实很好玩 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
寻找中位数
LeetCode 295. Find Median from Data Stream 设计一个数据结构,该数据结构动态维护一组数据,且支持如下操作: 1.添加元素: void addNum(int num),将整型num添加至数据结构中。 2.返回数据的中位数: double findMedian(),返回其维护的数据的中位数。 中位数定义: 1.若数据个数为奇数,中位数是该组数排序后中间的数。[1,2,3] -> 2 2.若数据个数为偶数,中位数是该组数排序后中间的两个数字的平均值。[1,2,3,4] -> 2.5
小飞侠xp
2018/08/29
1.3K0
【优先级队列】前K个高频单词 && 数据流的中位数
​ 给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
利刃大大
2025/03/16
440
【优先级队列】前K个高频单词 && 数据流的中位数
LeetCode-面试题41-数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
benym
2022/07/14
2090
大小堆解决【数据流中位数】问题,nice 图解~
在数据流中,数据会不断涌入结构中,那么也就面临着需要多次动态调整以获得中位数。 因此实现的数据结构需要既需要快速找到中位数,也需要做到快速调整。
掘金安东尼
2022/09/19
6050
大小堆解决【数据流中位数】问题,nice 图解~
LeetCode 295.数据流的中位数 - JavaScript
题目描述:中位数是有序列表中间的数。如果列表长度是偶数,中位数则是中间两个数的平均值。
心谭博客
2020/04/21
7180
【python-leetcode295-双堆】数据流的中位数
void addNum(int num) - 从数据流中添加一个整数到数据结构中。 double findMedian() - 返回目前所有元素的中位数。 示例:
西西嘛呦
2020/08/26
6260
【python-leetcode295-双堆】数据流的中位数
【LeetCode热题100】【堆】数据流的中位数
不停插入元素要求找到每个状态的中位数,用两个堆,把中位数左边的数记为left,右边的数记为right,一个大顶堆记录小于等于中位数的left,一个小顶堆记录大于中位数的right,数组长度为奇数时大顶堆比小顶堆多一个中位数,数组长度为偶数时,中位数就是两个堆顶的平均值
叶茂林
2024/04/18
810
一道简约而不简单的算法题--数据流的中位数
题目来源于 LeetCode 上第 295 号问题:数据流的中位数。难度级别为 Hard,目前通过率为 33.5% 。
五分钟学算法
2019/05/06
6640
一道简约而不简单的算法题--数据流的中位数
LeetCode 295. 数据流的中位数(大小堆)
类似题目: LeetCode 480. 滑动窗口中位数(大小堆升级版+set实现) LeetCode 703. 数据流中的第K大元素(优先队列)
Michael阿明
2020/07/13
5930
LeetCode 295. 数据流的中位数(大小堆)
啊这,一道找中位数的算法题把东哥整不会了…
如果输入一个数组,让你求中位数,这个好办,排个序,如果数组长度是奇数,最中间的一个元素就是中位数,如果数组长度是偶数,最中间两个元素的平均数作为中位数。
labuladong
2021/09/23
1.1K0
剑指Offer题解 - Day38
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。
chuckQu
2022/08/19
2190
Day6-线性表-堆-数组中第K大的数
一 先唠两句 下午从5点开始需求评审开了仨小时,去食堂吃完饭回来又赶紧干活,毕竟节前要上线,最近确实有点忙的手忙脚乱,盒饭更的稍微简短一些,不过干货肯定还是少不了的??? 二 直接上题 Q
BUPTrenyi
2019/07/16
7070
Day6-线性表-堆-数组中第K大的数
程序员进阶之算法练习(三十四)LeetCode专场
LeetCode上的题目是大公司面试常见的算法题,今天的目标是拿下5道算法题: 1、2、3题都是Medium的难度,大概是头条的面试题水准; 4、5题是Hard的难度,但是可以用取巧的做法,实现难度降到Medium和Easy的难度;
落影
2018/10/20
9220
每日算法题:Day 31(Linux)
给定一棵二叉搜索树,请找出其中的第k小的结点。例如, (5,3,7,2,4,6,8)中,按结点数值大小顺序第三小结点的值为4。
算法工程师之路
2019/09/10
4880
LeetCode 295. Find Median from Data Stream (堆)
求一个数组的中位数,但是这个数组是动态增加的,怎么做呢?可以考虑到用插入排序,每增加一个值,都插入排序一下,最坏的效率是O(n),查询效率是O(1) 效率太低,会超时。更高明的做法,是维护两个堆,一个是大堆,一个是小堆,大堆的数字都大于小堆里的数字,两个堆的数字均分这个数字。大堆用最小堆实现,小堆用最大堆实现。 当插入一个数,我们把它跟大堆的堆顶,也就大数字里的最小数对比,要是比它大,就入大堆,要是比它小,就入小堆,之后再调整两个堆,保证平均,调整只要比较堆顶的数字即可。
ShenduCC
2020/05/27
3740
算法思想总结:优先级队列
我们每次都要快速找到前两个最大的石头进行抵消,这个时候用优先级队列(建大堆),不断取堆顶元素是最好的!每次删除堆顶元素后,可以自动调整,时间复杂度是logN。
小陈在拼命
2024/07/16
1970
算法思想总结:优先级队列
数据结构与算法(4)——优先队列和堆什么是优先队列?堆和二叉堆LeetCode相关题目整理
听这个名字就能知道,优先队列也是一种队列,只不过不同的是,优先队列的出队顺序是按照优先级来的;在有些情况下,可能需要找到元素集合中的最小或者最大元素,可以利用优先队列ADT来完成操作,优先队列ADT是一种数据结构,它支持插入和删除最小值操作(返回并删除最小元素)或删除最大值操作(返回并删除最大元素);
我没有三颗心脏
2018/07/24
1.3K0
数据结构与算法(4)——优先队列和堆什么是优先队列?堆和二叉堆LeetCode相关题目整理
[剑指offer] 数据流中的中位数
如何得到一个数据流中的中位数?如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。如果从数据流中读出偶数个数值,那么中位数就是所有数值排序之后中间两个数的平均值。我们使用Insert()方法读取数据流,使用GetMedian()方法获取当前读取数据的中位数。
尾尾部落
2018/09/04
8320
数据流中的中位数,确实轻敌了
大家好,我是bigsai,今天忙到爆炸(暂不透露以后透露),给大家分享一个巧妙的问题,五分钟掌握。
bigsai
2021/07/22
6360
堆的基础知识
二叉堆是非线性的树形的数据结构,有两种堆,最大堆与最小堆。最大堆,树种各个父 节点的值总是大于或等于任何一个子节点的值;最小堆,树种各个父节点的值总是小于或 等于任何一个子节点的值。我们一般使用二叉堆来实现优先级队列,它的内部调整算法复杂 度为log(N),标准STL的优先级队列包括如下5种操作,设堆H: 1.取出堆顶元素:H.top(); 2.判断堆是否为空:H.empty(); 3.将元素x添加至堆:H.push(x) 4.弹出堆顶:H.pop(); 5.求堆存储元素的个数:H.size()
小飞侠xp
2018/08/29
3450
推荐阅读
相关推荐
寻找中位数
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档