剑指 Offer(第2版)面试题 41:数据流的中位数

news2024/11/16 19:58:58

剑指 Offer(第2版)面试题 41:数据流的中位数

  • 剑指 Offer(第2版)面试题 41:数据流的中位数
    • 解法1:优先队列
    • 解法2:有序集合 + 双指针

剑指 Offer(第2版)面试题 41:数据流的中位数

题目来源:LeetCode 295. 数据流的中位数

如何得到一个数据流中的中位数?

如果从数据流中读出奇数个数值,那么中位数就是所有数值排序之后位于中间的数值。

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

解法1:优先队列

我们用两个优先队列 minHeap 和 maxHeap 分别记录大于中位数的数和小于等于中位数的数。其中,maxHeap 是大顶堆,top 是其最大的元素;minHeap 是小顶堆,top 是其最小的元素。

void addNum(int num):

当我们尝试添加一个数 num 到数据结构中,我们直接插入到 maxHeap 中。当 maxHeap.size() > minHeap.size() + 1 时,我们需要将 maxHeap.top() 移动到 minHeap 中。另外,当 maxHeap.top() > minHeap.top() 且 minHeap 不为空时,我们需要 swap(maxHeap.top(), minHeap.top()) 直至 maxHeap.top() <= minHeap.top()。

注意:

  1. 在交换 2 个堆的元素的时候,一定要 先判断上面的小根堆中有没有元素。小根堆中没有元素是不能交换的。
  2. 小根堆没有元素,就把大根堆中的 top 放到小根堆中。

double findMedian():

  • 当累计添加的数的数量为奇数时,maxHeap 中的数的数量比 minHeap 多一个,此时中位数为 maxHeap 的队头。
  • 当累计添加的数的数量为偶数时,两个优先队列中的数的数量相同,此时中位数为它们的队头的平均值。

代码:

/*
 * @lc app=leetcode.cn id=295 lang=cpp
 *
 * [295] 数据流的中位数
 */

// @lc code=start
class MedianFinder
{
private:
    // 大顶堆,top 是其最大的元素,存储小于等于中位数的数
    priority_queue<int, vector<int>, less<int>> maxHeap;
    // 小顶堆,top 是其最小的元素,存储大于中位数的数
    priority_queue<int, vector<int>, greater<int>> minHeap;

public:
    MedianFinder() {}

    void addNum(int num)
    {
        maxHeap.push(num);
        if (maxHeap.size() > minHeap.size() + 1)
        {
            int x = maxHeap.top();
            minHeap.push(x);
            maxHeap.pop();
        }
        while (!minHeap.empty() && maxHeap.top() > minHeap.top())
        {
            int x = maxHeap.top(), y = minHeap.top();
            maxHeap.pop();
            maxHeap.push(y);
            minHeap.pop();
            minHeap.push(x);
        }
    }

    double findMedian()
    {
        double median;
        if ((maxHeap.size() + minHeap.size()) % 2 == 1)
            median = (double)maxHeap.top();
        else
            median = (maxHeap.top() + minHeap.top()) / 2.0;
        return median;
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */
// @lc code=end

复杂度分析:

时间复杂度:

  • addNum:O(log⁡n),其中 n 为累计添加的数的数量。
  • findMedian:O(1)。

空间复杂度:O(n),主要为优先队列的开销。

解法2:有序集合 + 双指针

我们也可以使用有序集合维护这些数。我们把有序集合看作自动排序的数组,使用双指针指向有序集合中的中位数元素即可。当累计添加的数的数量为奇数时,双指针指向同一个元素。当累计添加的数的数量为偶数时,双指针分别指向构成中位数的两个数。

当我们尝试添加一个数 num 到数据结构中,我们需要分情况讨论:

  1. 初始有序集合为空时,我们直接让左右指针指向 num 所在的位置。
  2. 有序集合为中元素为奇数时,left 和 right 同时指向中位数。如果 num 小于中位数,那么只要让 left 左移,否则让 right 右移。
  3. 有序集合为中元素为偶数时,left 和 right 分别指向构成中位数的两个数。
    • 当 num 成为新的唯一的中位数,那么我们让 left 右移,right 左移,这样它们即可指向 num 所在的位置;
    • 当 num 大于等于 right 指向的值,那么我们让 left 右移即可;
    • 当 num 小于 right 指向的值,那么我们让 right 左移,注意到如果 num 恰等于 left 指向的值,那么 num 将被插入到 left 右侧,使得 left 和 right 间距增大,所以我们还需要额外让 left 指向移动后的 right。

代码:

// 有序集合 + 双指针

class MedianFinder
{
private:
    multiset<int> nums;
    multiset<int>::iterator left, right;

public:
    MedianFinder() : left(nums.end()), right(nums.end()) {}

    void addNum(int num)
    {
        const size_t n = nums.size();

        nums.insert(num);
        if (n == 0)
        {
            left = right = nums.begin();
        }
        else if (n % 2 == 1)
        {
            if (num < *left)
                left--;
            else
                right++;
        }
        else
        {
            if (num > *left && num < *right)
            {
                left++;
                right--;
            }
            else if (num >= *right)
                left++;
            else
            {
                right--;
                left = right;
            }
        }
    }

    double findMedian()
    {
        return (*left + *right) / 2.0;
    }
};

复杂度分析:

时间复杂度:

  • addNum:O(log⁡n),其中 n 为累计添加的数的数量。
  • findMedian:O(1)。

空间复杂度:O(n),主要为有序集合的开销。

一些进阶小 tips:

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1323312.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

控制理论simulink+matlab

这里写目录标题 根轨迹二级目录三级目录 根轨迹 z [-1]; %开环传递函数的零点 p [0 -2 -3 -4]; %开环传递函数的系统极点 k 1; %开环传递函数的系数&#xff0c;反映在比例上 g zpk(z,p,k); %生成开环传递函数%生成的传递函数如下 % (s1) % -------------…

【HarmonyOS开发】ArkUI中的自定义弹窗

弹窗是一种模态窗口&#xff0c;通常用来展示用户当前需要的或用户必须关注的信息或操作。在弹出框消失之前&#xff0c;用户无法操作其他界面内容。ArkUI 为我们提供了丰富的弹窗功能&#xff0c;弹窗按照功能可以分为以下两类&#xff1a; 确认类&#xff1a;例如警告弹窗 Al…

GitBook安装及使用——使用 Markdown 创建你自己的博客网站和电子书

目录 前言一、依赖环境二、gitbook安装使用1.安装 gitbook-cli2.安装 gitbook3.Gitbook初始化4.创建你的文章5.修改 SUMMARY.md 和 README.md6.编译生成静态网页7.运行以便在浏览器预览8.运行效果 前言 GitBook是一个命令行工具&#xff0c;用于使用 Markdown 构建漂亮的博客网…

npm login报错:Public registration is not allowed

npm login报错:Public registration is not allowed 1.出现场景2.解决 1.出现场景 npm login登录时,出现 2.解决 将自己的npm镜像源改为npm的https://registry.npmjs.org/这个&#xff0c;解决&#xff01;

鸿蒙4.0核心技术-WebGL开发

场景介绍 WebGL主要帮助开发者在前端开发中完成图形图像的相关处理&#xff0c;比如绘制彩色图形等。 接口说明 表1 WebGL主要接口列表 接口名描述canvas.getContext获取canvas对象上下文。webgl.createBuffer(): WebGLBuffernullwebgl.bindBuffer(target: GLenum, buffer: …

服务器数据恢复-EMC存储raid5磁盘物理故障离线的数据恢复案例

服务器数据恢复环境&故障&#xff1a; 一台emc某型号存储服务器&#xff0c;存储服务器上组建了一组raid5磁盘阵列&#xff0c;阵列中有两块磁盘作为热备盘使用。存储服务器在运行过程中有两块磁盘出现故障离线&#xff0c;但是只有一块热备盘激活&#xff0c;最终导致该ra…

Gin之GORM多表关联查询(多对多;自定义预加载SQL)

数据库三个,如下: 注意:配置中间表的时候,表设计层面最好和配置的其他两张表契合,例如其他两张表为fate内的master和slave;要整合其对应关系的话,设计中间表的结构为master_id和slave_id最好(不然会涉及重写外键的操作) 重写外键(介绍) 对于 many2many 关系,连接表…

智能优化算法应用:基于黑寡妇算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于黑寡妇算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于黑寡妇算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.黑寡妇算法4.实验参数设定5.算法结果6.参考文…

Jenkins Docker Cloud在Linux应用开发CI中的实践

Jenkins Docker Cloud在Linux应用开发CI中的实践 背景 通过代码提交自动触发CI自动构建、编译、打包是任何软件开发组织必不可少的基建&#xff0c;可以最大程度保证产物的一致性&#xff0c;方便跨组跨部门协作&#xff0c;代码MR等。 Docker在流水线中越来越重要&#xff…

iPhone手机开启地震预警功能

iPhone手机开启地震预警功能 地震预警告警开启方式 地震预警 版权&#xff1a;成都高新减灾研究所 告警开启方式

蜘点云原生之 KubeSphere 落地实践过程

作者&#xff1a;池晓东&#xff0c;蜘点商业网络服务有限公司技术总监&#xff0c;从事软件开发设计 10 多年&#xff0c;喜欢研究各类新技术&#xff0c;分享技术。 来源&#xff1a;本文由 11 月 25 日广州站 meetup 中讲师池晓东整理&#xff0c;整理于该活动中池老师所分享…

YOLOv8改进 | 主干篇 | 轻量级网络ShuffleNetV2(附代码+修改教程)

一、本文内容 本文给大家带来的改进内容是ShuffleNetV2&#xff0c;这是一种为移动设备设计的高效CNN架构。其在ShuffleNetV1的基础上强调除了FLOPs之外&#xff0c;还应考虑速度、内存访问成本和平台特性。(我在YOLOv8n上修改该主干降低了GFLOPs,但是参数量还是有一定上涨&am…

【Docker】基础篇

文章目录 Docker为什么出现容器和虚拟机关于虚拟机关于Docker二者区别&#xff1a; Docker的基本组成相关概念-镜像&#xff0c;容器&#xff0c;仓库安装Docker卸载docker阿里云镜像加速docker run的原理**为什么容器比虚拟机快**Docker的常用命令1.帮助命令2.镜像相关命令3.容…

C语言—每日选择题—Day51

第一题 1. 对于函数void f(int x);&#xff0c;下面调用正确的是&#xff08;&#xff09; A&#xff1a;int y f(9); B&#xff1a;f(9); C&#xff1a;f( f(9) ); D&#xff1a;xf(); 答案及解析 B 函数调用要看返回值和传参是否正确&#xff1b; A&#xff1a;错误&#xf…

【ArcGIS微课1000例】0081:ArcGIS指北针乱码解决方案

问题描述&#xff1a; ArcGIS软件在作图模式下插入指北针&#xff0c;出现指北针乱码&#xff0c;如下图所示&#xff1a; 问题解决 下载并安装字体&#xff08;配套实验数据包0081.rar中获取&#xff09;即可解决该问题。 正常的指北针选择器&#xff1a; 专栏介绍&#xff…

Hadoop3.x完全分布式模式下slaveDataNode节点未启动调整

目录 前言 一、问题重现 1、查询Hadoop版本 2、集群启动Hadoop 二、问题分析 三、Hadoop3.x的集群配置 1、停止Hadoop服务 2、配置workers 3、从节点检测 4、WebUI监控 总结 前言 在大数据的世界里&#xff0c;Hadoop绝对是一个值得学习的框架。关于Hadoop的知识&…

elementui中的el-table,当使用fixed属性时,table主体会遮挡住滚动条的大半部分,导致很难选中。

情况&#xff1a; 解决&#xff1a; el-table加个类&#xff0c;这里取为class"table" 然后是样式部分&#xff1a; <style scoped lang"scss"> ::v-deep.table {// 滚动条高度调整::-webkit-scrollbar {height: 15px;}// pointer-events 的基本信…

一款电压检测LVD

一、基本概述 The TX61C series devices are a set of three terminal low power voltage detectors implemented in CMOS technology. Each voltage detector in the series detects a particular fixed voltage ranging from 0.9V to 5.0V. The voltage detectors consist…

使用Axure的中继器的交互动作解决增删改查h

&#x1f3ac; 艳艳耶✌️&#xff1a;个人主页 &#x1f525; 个人专栏 &#xff1a;《产品经理如何画泳道图&流程图》 ⛺️ 越努力 &#xff0c;越幸运 目录 一、中继器的交互 1、什么是中继器的交互 2、Axure中继器的交互 3、如何使用中继器&#xff1f; 二…

✺ch7——光照

目录 光照模型光源材质ADS光照计算实现ADS光照Gouraud着色&#xff08;双线性光强插值法&#xff09; Phong着色Blinn-Phong反射模型结合光照与纹理补充说明 光照模型 光照模型(lighting model)有时也称为着色模型(shading model)&#xff0c;在着色器编程存在的情况下&#x…