《剑指 Offer》专项突破版 - 面试题 59、60 和 61 : 详解堆的应用(C++ 实现)

目录

前言

面试题 59 : 数据流的第 k 大数字

面试题 60 : 出现频率最高的 k 个数字

面试题 61 : 和最小的 k 个数对


 


前言

堆最大的特点是最大值或最小值位于堆的顶部,只需要 O(1) 的时间就可以求出一个数据集合中的最大值或最小值,同时在堆中添加或删除元素的时间复杂度都是 O(logn),因此综合来看堆是一个比较高效的数据结构。如果面试题需要求出一个动态数据集合中的最大值或最小值,那么可以考虑使用堆来解决问题。

堆经常用来求取一个数据集合中值最大或最小的 k 个元素。通常,最小堆用来求取数据集合中 k 个值最大的元素,最大堆用来求取数据集合中 k 个值最小的元素

接下来使用最小堆或最大堆解决几道典型的算法面试题。


面试题 59 : 数据流的第 k 大数字

题目

请设计一个类型 KthLargest,它每次从一个数据流中读取一个数字,并得出数据流已经读取的数字中第 k(k >= 1)大的数字。该类型的构造函数有两个参数:一个是整数 k,另一个是包含数据流中最开始数字的整数数组 nums。该类型还有一个函数 add,用来添加数据流中的新数字并返回数据流中已经读取的数字的第 k 大数字。

例如,当 k = 3 且 nums 为数组 [4, 5, 8, 2] 时,调用构造函数创建类型 KthLargest 的实例之后,第 1 次调用 add 函数添加数字 3,此时已经从数据流中读取了数字 4、5、8、2 和 3,第 3 大的数字是 4;第 2 次调用 add 函数添加数字 5 时,则返回第 3 大的数字 5。

分析

与数据流相关的题目的特点是输入的数据是动态添加的,也就是,可以不断地从数据流中读取新的数据,数据流的数据量是无限的。在这个题目中,类型 KthLargest 的函数 add 用来添加从数据流中读取的新数据。

解决这个题目的关键在于选择合适的数据结构。如果数据存储在排序的数组中,那么只需要 O(1) 的时间就能找出第 k 大的数字。但这个直观的方法有两个缺点。首先,需要把从数据流中读取的所有数据都存到排序数组中,如果从数据流中读取 n 个数字,那么动态数组的大小为 O(n)。随着不断地从数据流中读取新的数据,O(n) 的空间复杂度可能会耗尽所有的内存。其次,在排序数组中添加新的数字的时间复杂度也是 O(n)

下面换一个角度看待第 k 大的数字。如果能够找出 k 个最大的数字,那么第 k 大的数字就是这 k 个最大数字中最小的一个。例如,从数据流中已经读出了 4、5、8、2、3 这 5 个数字,其中最大的 3 个数字是 4、5、8。这 3 个数字的最小值 4 就是 4、5、8、2、3 这 5 个数字中第 3 大的数字。

由于每次都需要找出 k 个数字中的最小值,因此可以把这 k 个数字保存到最小堆中。每当从数据流中读出一个数字,就先判断这个新的数字是不是有必要添加到最小堆中

  1. 如果最小堆中元素的数目还小于 k,那么直接将它添加到最小堆中

  2. 如果最小堆中已经有 k 个元素,那么将其和位于堆顶的最小值进行比较。如果新读出的数字小于或等于堆中的最小值,那么堆中的 k 个数字都比它大,因此它不可能是 k 个最大的数字中的一个。由于只需要保存最大的 k 个数字,因此新读出的数字可以忽略。如果新的数字大于堆顶的数字,那么堆顶的数字就是第 k + 1 大的数字,可以将它从堆中删除,并将新的数字添加到堆中,这样堆中保存的仍然是到目前为止从数据流中读出的最大的 k 个数字,此时第 k 大的数字正好位于最小堆的堆顶

代码实现

class KthLargest {
public:KthLargest(int k, vector<int>& nums) : capacity(k) {for (int num : nums){add(num);}}int add(int val) {if (minHeap.size() < capacity){minHeap.push(val);}else if (val > minHeap.top()){minHeap.pop();minHeap.push(val);}return minHeap.top();}
private:priority_queue<int, vector<int>, greater<int>> minHeap;size_t capacity;
};

假设数据流中总共有 n 个数字。这种解法特别适合 n 远大于 k 的场景。当 n 非常大时,内存可能不能容纳数据流中的所有数字。但使用最小堆之后,内存中只需要保存 k 个数字,空间效率非常高


面试题 60 : 出现频率最高的 k 个数字

题目

请找出数组中出现频率最高的 k 个数字。例如,当 k 等于 2 时,输入数组 [1, 2, 2, 1, 3, 1],由于数字 1 出现了 3 次,数字 2 出现了 2 次,数字 3 出现了 1 次,因此出现频率最高的 2 个数字是 1 和 2。

分析

如果在面试过程中遇到这个题目,首先要想到的是解决这个题目需要用到哈希表。这个题目的输入是一个数组,哈希表可以用来统计数组中数字出现的频率,哈希表的键是数组中出现的数字,而值是数字出现的频率

接下来找出出现频率最高的 k 个数字。可以用一个最小堆存储出现频率最高的 k 个数字,堆中的每个元素是数组中的数字及其在数组中出现的次数(即哈希表中数字到频率的映射)。由于比较的是数字的频率,因此设置最小堆比较元素的规则,以便让频率最低的数字位于堆的顶部

代码实现

struct GreaterCmpByCnt
{bool operator()(const pair<int, int>& lhs, const pair<int, int>& rhs) const{return lhs.second > rhs.second;}
};
​
class Solution {
public:vector<int> topKFrequent(vector<int>& nums, int k) {unordered_map<int, int> numToCount;for (int num : nums){++numToCount[num];}
​priority_queue<pair<int, int>, vector<pair<int, int>>, GreaterCmpByCnt> minHeap;for (const pair<int, int>& kv : numToCount){if (minHeap.size() < k){minHeap.push(kv);}else if (minHeap.top().second < kv.second){minHeap.pop();minHeap.push(kv);}}
​vector<int> result(k);for (int i = 0; i < k; ++i){result[i] = minHeap.top().first;minHeap.pop();}return result;}
};


面试题 61 : 和最小的 k 个数对

题目

给定两个递增排序的整数数组,从两个数组中各取一个数字 u 和 v 组成一个数对 (u, v),请找出和最小的 k 个数对。例如,输入两个数组 [1, 5, 13, 21] 和 [2, 4, 9, 15],和最小的 3 个数对为 (1, 2)、(1, 4) 和 (2, 5)。

分析

假设第 1 个数组 nums1 的长度为 m,第 2 个数组 nums2 的长度为 n,那么从两个数组中各取一个数字能组成 m x n 个数对。

这个题目要求找出和最小的 k 个数对。可以用最大堆来存储这个 k 个和最小的数对。逐一将 m x n 个数对添加到最大堆中

题目给出的条件是输入的两个数组都是递增排序的,这个特性我们还没有用到。如果从第 1 个数组中选出第 k + 1 个数字和第 2 个数组中的某个数字组成数对 p,那么该数对之和一定不是和最小的 k 个数对中的一个,这是因为第 1 个数组中的前 k 个数字和第 2 个数组中的同一个数字组成的 k 个数对之和都要小于数对 p 之和。因此,不管输入的数组 nums1 有多长,最多只考虑前 k 个数字。同理,不管输入的数组 nums2 有多长,最多也只考虑前 k 个数字。

代码实现

struct LessCmpBySum {bool operator()(const vector<int>& lhs, const vector<int>& rhs) {return lhs[0] + lhs[1] < rhs[0] + rhs[1];}
};
​
class Solution {
public:vector<vector<int>> kSmallestPairs(vector<int>& nums1, vector<int>& nums2, int k) {priority_queue<vector<int>, vector<vector<int>>, LessCmpBySum> maxHeap;int m = min((int)nums1.size(), k), n = min((int)nums2.size(), k);for (int i = 0; i < m; ++i){for (int j = 0; j < n; ++j){if (maxHeap.size() < k){maxHeap.push({ nums1[i], nums2[j] });}else if (nums1[i] + nums2[j] < maxHeap.top()[0] + maxHeap.top()[1]){maxHeap.pop();maxHeap.push({ nums1[i], nums2[j] });}}}
​vector<vector<int>> result;while (!maxHeap.empty()){result.push_back(maxHeap.top());maxHeap.pop();}return result;}
};

上述代码有两个相互嵌套的 for 循环,每个循环最多执行 k 次(假设数组 num1 和 num2 的长度都大于或等于 k)。在循环体内可能在最大堆中进行添加或删除操作,由于最大堆中最多包含 k 个元素,因此添加、删除操作的时间复杂度都是 O(logk)。这两个 for 循环的时间复杂度是 O(k^2 * logk)。另外,上述代码还有一个 while 循环,它逐一从最大堆中删除元素并将对应的数对添加到 result 数组中,这个 while 循环的时间复杂度是 O(klogk)。因此,上述代码总的时间复杂度是 O(k^2 * logk)

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

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

相关文章

金融保险领域统一消息发送平台

项目介绍&#xff1a;系统为金融保险行业统一推送基础平台&#xff0c;日均推送消息2000万条&#xff0c;支持短信、官微、邮件多种发送渠道。 项目定位&#xff1a;结合公司项目产品化战略&#xff0c;从基础的推送能力出发&#xff0c;形成公司的统一推送基础产品 项目功能…

STM32F103学习笔记(六) RTC实时时钟(应用篇)

目录 1. RTC 实时时钟的应用场景 2. RTC 的配置与初始化 2.1 设置 RTC 时钟源 2.2 初始化 RTC 寄存器 2.3 中断配置 2.4 备份寄存器配置 2.5 校准 RTC 3. 实例演示代码 4. 总结 1. RTC 实时时钟的应用场景 实时时钟&#xff08;RTC&#xff09;在嵌入式系统中具有广泛…

用C#开发Excel插件的强大开源工具

推荐一个开源项目&#xff0c;方便我们使用C#为Excel开发插件。 01 项目简介 Excel-DNA是一个.Net开源项目&#xff0c;为开发者提供了一种便利的方法&#xff0c;可以将.Net代码与Excel集成&#xff0c;能够轻松的为Excel创建自定义函数、图表、表单等&#xff0c;一方面不仅…

VS2022调试技巧(一)

什么是bug&#xff1f; 在1945年&#xff0c;美国科学家Grace Hopper在进行计算机编程时&#xff0c;发现一只小虫子钻进了一个真空管&#xff0c;导致计算机无法正常工作。她取出虫子后&#xff0c;计算机恢复了正常&#xff0c;由此&#xff0c;她首次将“Bug”这个词用来描…

【C语言基础】:操作符详解(二)

文章目录 操作符详解一、上期扩展二、单目操作符三、逗号表达式四、下标访问[]、 函数调用()五、结构成员访问操作符六、操作符的属性&#xff1a;优先级、结合性1. 优先级2. 结合性 操作符详解 上期回顾&#xff1a;【C语言基础】&#xff1a;操作符详解(一) 一、上期扩展 …

https://htmlunit.sourceforge.io/

https://htmlunit.sourceforge.io/ 爬虫 HtmlUnit – Welcome to HtmlUnit HtmlUnit 3.11.0 API https://mvnrepository.com/artifact/net.sourceforge.htmlunit/htmlunit/2.70.0 https://s01.oss.sonatype.org/service/local/repositories/releases/content/org/htmlunit…

进程间通信学习笔记(有名管道和无名管道)

进程间通信方式&#xff1a; 无名管道(pipe)有名管道(fifo)信号(signal)共享内存(mmap)套接字(socket) 无名管道&#xff1a; 在内核里面开辟一片内存&#xff0c;进程1和进程2都可以通过这片内存进行通信 无名管道特点&#xff1a; 只能用于具有亲缘关系的进程之间的通信&am…

YOLOv9图像标注和格式转换

一、软件安装 labelimg安装&#xff08;anaconda&#xff09; 方法一、 pip install labelImg 方法二、 pip install PyQt5 -i https://pypi.tuna.tsinghua.edu.cn/simple/ pip install pyqt5-tools -i https://pypi.tuna.tsinghua.edu.cn/simple/ pip install lxml -i ht…

从Unity到Three.js(shader创建)

本文介绍three.js 中shader的创建和应用到模型&#xff0c;具体shader的语法和函数应用&#xff0c;已经有很多大佬总结过了。 three.js shader详解 import * as THREE from three;const scene new THREE.Scene();//创建场景 scene.backgroundnew THREE.Color(0,0.1,0.2,1);/…

系统找不到xinput1_3.dll怎么办?试试这五种解决方法轻松搞定

在计算机系统运行过程中&#xff0c;当我们遭遇“找不到xinput1_3.dll”这一错误提示时&#xff0c;实际上正面临一个软件兼容性、系统组件缺失以及游戏或应用程序无法正常启动的关键问题。深入探究这一现象&#xff0c;我们会发现它可能引发一系列连带问题&#xff0c;例如某些…

linux之前后端项目部署与发布

目录 前言 简介 一、安装Nginx 二、后端部署 2.1多个tomcat负载均衡 2.2 负载均衡 2.3 后端项目部署 三、前端部署 1.解压前端 2.Nginx配置文件修改 3.IP域名映射 4.重启Nginx服务 前言 上篇博主已经讲解过了单机项目的部署linux之JAVA环境配置JDK&Tomcat&a…

车载终端_联发科MTK6762车载平板电脑解决方案

智能车载终端方案搭载了MTK联发科8xARM Cortex-A53(64bit)高速CPU&#xff0c;采用12nm工艺制程&#xff0c;提供更快的数据采集速度和APP响应速度&#xff0c;能够快速满足用户的应用需求。配备3GB RAM32GB ROM的低功耗EMCP一体化存储&#xff0c;性能良好&#xff0c;支持多任…

【LeetCode:2476. 二叉搜索树最近节点查询 + 中序遍历 + 有序表】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

panelRef.value.getBoundingClientRect is not a function

<el-cascader-panel v-model"value" ref"panelRef" expand-change"expandChange" />const panelRef ref(null) panelRef.value.getBoundingClientRect()报错&#xff1a; panelRef.value.getBoundingClientRect is not a function 报错原…

python使用winio控制x86工控机的gpio

视频讲解 https://www.bilibili.com/video/BV1Nu4m1w7iv/?vd_source5ba34935b7845cd15c65ef62c64ba82f pywinio库 https://pypi.org/project/pywinio/ 安装库 pip install pywinio寄存器地址 测试代码 import pywinio winio get_winio() # 设置排针2输出1,0x40是bit6置…

嵌入式中逻辑分析仪基本操作方法

前期准备 1.一块能触摸的屏对应的主板机 2.逻辑分析仪对应的软件工具 3.对应的拓展板 4.确定拓展板的引脚分布情况 第一步&#xff1a;逻辑分析仪j基本操作 1.数据捕捉需要先进行对应软件安装,并按照需求进行配置 2.这里以A20为例:此手机使用显示驱动芯片CST148,触摸屏分辨…

插件废土课:打造属于你的“智能笔记”!

哎呀嘞&#xff0c;亲爱的网页冲浪者们&#xff0c;抓紧浮板&#xff0c;我们要继续在Chrome插件的海浪上翻滚啦&#xff01;上次我们玩了个小把戏&#xff0c;搞了个显示时间的Hello World插件&#xff0c;这次我们要把游戏玩大&#xff0c;准备打造一个能让你在网页上乱涂乱画…

Onlyfans信用卡支付失败怎么办?怎么订阅Onlyfans

OnlyFans信用卡支付失败解决方案及订阅指南 简介 OnlyFans是一个以内容创作者为重点的订阅平台&#xff0c;让用户可以通过支付订阅费用来获取独家内容。然而&#xff0c;有时候在进行信用卡支付时可能会遇到一些问题。本篇文章将为你提供解决OnlyFans信用卡支付失败的方案&a…

数学建模【GM(1, 1)灰色预测】

一、GM(1, 1)灰色预测简介 乍一看&#xff0c;这个名字好奇怪&#xff0c;其实是有含义的 G&#xff1a;Grey&#xff08;灰色&#xff09;M&#xff1a;Model&#xff08;模型&#xff09;(1, 1)&#xff1a;只含有一个变量的一阶微分方程模型 提到灰色&#xff0c;就得先说…

大数据开发项目--音乐排行榜

环境&#xff1a;windows10&#xff0c;centos7.9&#xff0c;hadoop3.2、hbase2.5.3和zookeeper3.8完全分布式&#xff1b; 环境搭建具体操作请参考以下文章&#xff1a; CentOS7 Hadoop3.X完全分布式环境搭建 Hadoop3.x完全分布式环境搭建Zookeeper和Hbase 1. 集成MapReduce…