《剑指 Offer》专项突破版 - 面试题 38、39 和 40 : 通过三道面试题详解单调栈(C++ 实现)

目录

面试题 38 : 每日温度

面试题 39 : 直方图最大矩形面积

方法一、暴力求解

方法二、递归求解

方法三、单调栈法

面试题 40 : 矩阵中的最大矩形


 


面试题 38 : 每日温度

题目

输入一个数组,它的每个数字是某天的温度。请计算每天需要等几天才会出现更高的温度。例如,如果输入数组 [35, 31, 33, 36, 34],那么输出为 [3, 1, 1, 0, 0]。由于第 1 天的温度是 35℃,要等 3 天才会出现更高的温度 36℃,因此对应的输出为 3。第 4 天的温度是 36℃,后面没有更高的温度,它对应的输出是 0。其他的以此类推。

分析

解决这个问题的直观方法很多人很快就能想到。对于数组中的每个温度,可以扫描它后面的温度直到发现一个更高的温度为止。如果数组包含 n 天的温度,那么这种思路的时间复杂度是 O(n^2)。

下面通过一个具体的例子来分析这个问题的规律。假设输入的表示每天的温度的数组为 [35, 31, 33, 36, 34]。第 1 天的温度是 35℃,此时还不知道后面会不会有更高的温度,所以先将它保存到一个数据容器中。第 2 天的温度是 31℃,比第 1 天温度低,同样也保存到数据容器中。第 3 天的温度是 33℃,比第 2 天的温度高,由此可知,第 2 天需要等 1 天才有更高的温度。

每次从数组中读取某一天的温度,并且都将其与之前的温度(也就是已经保存在数据容器中的温度)相比较。从离它较近的温度开始比较看起来是一个不错的选择,也就是后存入数据容器中的温度先拿来比较,这契合 "后进先出" 的特性,所以可以考虑用栈来实现这个数据容器。同时,需要计算出现更高温度的等待天数,存入栈中的数据应该是温度在数组中的下标。等待的天数就是两个温度在数组中的下标之差

因此,处理到第 3 天的温度时,栈的状态为 [0, 1]。在知道第 2 天需要等 1 天将出现更高的温度之后,它就没有必要再保存在栈中,将它出栈。第 3 天的温度也需要入栈,以便和以后的温度比较,此时栈的状态为 [0, 2]。

第 4 天的温度是 36℃。从栈顶开始与之前的温度比较,它比第 3 天的温度 33℃ 高,因此第 3 天需要等 1 天就会出现更高的温度,这一天在数组中的下标 2 出栈。它也比第 1 天的温度 35℃ 高,因此第 1 天需要等 3 天才会出现更高的温度,这一天在数组中的下标 0 出现。然后将第 4 天在数组中的下标 3 入栈,以便和以后的温度比较。此时栈的状态为 [3]。最后一天的温度是 34℃,比位于栈顶的第 4 天的温度低,将其入栈,最终栈的状态是 [3, 4]。最终留在栈中的两天的后面都没有出现更高的温度。

解决这个问题的思路总结起来就是用一个栈保存每天的温度在数组中的下标。每次从数组中读取一个温度,然后将其与栈中保存的温度(根据下标可以得到温度)进行比较。如果当前温度比位于栈顶的温度高,那么就能知道位于栈顶那一天需要等待几天才会出现更高的温度。然后出栈 1 次,将当前温度与下一个位于栈顶的温度进行比较。如果栈中已经没有比当前温度低的温度,则将当前温度在数组中的下标入栈

代码实现

class Solution {
public:vector<int> dailyTemperatures(vector<int>& temperatures) {int n = temperatures.size();vector<int> result(n, 0);stack<int> st;for (int i = 0; i < n; ++i){while (!st.empty() && temperatures[i] > temperatures[st.top()]){result[st.top()] = i - st.top();st.pop();}
​st.push(i);}return result;}
};

保存在栈中的温度(通过数组下标可以得到温度)是递减排序的。这是因为如果当前温度比位于栈顶的温度高,位于栈顶的温度将出栈,所以每次入栈时当前温度一定比位于栈顶的温度低或相同

假设输入数组的长度为 n。虽然上述代码中有一个嵌套的二重循环,但它的时间复杂度是 O(n),这是因为数组中每个温度入栈、出栈各 1 次。这种解法的空间复杂度也是 O(n)。


面试题 39 : 直方图最大矩形面积

题目

直方图是由排列在同一基线上的相邻柱子组成的图形。输入一个由非负数组成的数组,数组中的数字是直方图中柱子的高。求直方图中最大矩形面积。假设直方图中柱子的宽都为 1。例如,输入数组 [3, 2, 5, 4, 6, 1, 4, 2],其对应的直方图如下图所示,该直方图中最大矩形面积为 12,如阴影部分所示。

分析

矩形的面积等于宽乘以高,因此只要能确定每个矩形的宽和高,就能计算它的面积。如果直方图中一个矩形从下标为 i 的柱子开始,到下标为 j 的柱子结束,那么这两根柱子之间的矩形(含两端的柱子)的宽是 j - i + 1。矩形的高就是两根柱子之间的所有柱子最矮的高度。例如,上图中从下标为 2 的柱子到下标为 4 的柱子之间的矩形宽度是 3,矩形的高度最多只能是 4,即它们之间 3 根柱子最矮的高度。

方法一、暴力求解

如果能逐一找出直方图中所有的矩形并比较它们的面积,就能得到最大矩形面积。下面使用嵌套的二重循环遍历所有矩形,并比较它们的面积。

class Solution {
public:int largestRectangleArea(vector<int>& heights) {int maxArea = 0;for (int i = 0; i < heights.size(); ++i){int minHeight = heights[i];for (int j = i; j < heights.size(); ++j){if (heights[j] < minHeight)minHeight = heights[j];int area = minHeight * (j - i + 1);
​if (area > maxArea)maxArea = area;}}return maxArea;}
};

这种解法的时间复杂度是 O(n^2),空间复杂度是 O(1)。

方法二、递归求解

上图的直方图中最矮的柱子在数组中的下标是 5,它的高度是 1。这个直方图的最大矩形有以下 3 种可能:

  1. 第 1 种是矩形通过这根最矮的柱子。通过最矮的柱子的最大矩形的高度是 1,宽度是 7

  2. 第 2 种是矩形的起始柱子和终止柱子都在最矮的柱子的左侧,也就是从下标为 0 的柱子到下标为 4 的柱子的直方图的最大矩形

  3. 第 3 种是矩形的起始柱子和终止柱子都在最矮的柱子的右侧,也就是从下标为 6 的柱子到下标为 7 的柱子的直方图的最大矩形

第 2 种和第 3 种本质上来说和求整个直方图的最大矩形面积是同一个问题,可以调用递归函数解决

class Solution {
private:int _largestRectangleArea(vector<int>& heights, int left, int right){if (left > right)return 0;if (left == right)return heights[left];
​int minHeightIndex = left;for (int i = left + 1; i <= right; ++i){if (heights[i] < heights[minHeightIndex])minHeightIndex = i;}int maxArea = heights[minHeightIndex] * (right - left + 1);int area1 = _largestRectangleArea(heights, left, minHeightIndex - 1);int area2 = _largestRectangleArea(heights, minHeightIndex + 1, right);if (area1 > maxArea)maxArea = area1;if (area2 > maxArea)maxArea = area2;return maxArea;}
​
public:int largestRectangleArea(vector<int>& heights) {return _largestRectangleArea(heights, 0, heights.size() - 1);}
};

假设输入数组的长度为 n。如果每次都能将 n 根柱子分成两根柱子数量为 n / 2 的子直方图,那么递归调用的深度为 O(logn),整个递归算法的时间复杂度是 O(nlogn)。但如果直方图中柱子的高度是排序的(递增排序或递减排序),那么每次最矮的柱子都位于直方图的一侧,递归调用的深度就是 O(n),此时递归算法的时间复杂度也变成 O(n^2)

递归算法的空间复杂度取决于调用栈的深度,因此平均空间复杂度是 O(logn),最坏情况下的空间复杂度是 O(n)

方法三、单调栈法

计算以每根柱子为顶的最大矩形面积,比较这些矩形面积就能得到直方图中的最大矩形面积

以某根柱子为顶的最大矩形,一定是从该柱子向两侧延伸直到遇到比它矮的柱子,这个最大矩形的高就是该柱子的高,最大矩形的宽是两侧比它矮的柱子中间的间隔。例如,为了求上图所示的直方图中以下标为 3 的柱子为顶的最大矩形面积,应该从该柱子开始向两侧延伸,左侧比它矮的柱子的下标是 1,右侧比它矮的柱子的下标是 5。因此,以下标为 3 的柱子为顶的最大矩形的高为 4,宽为 3(左右两侧比它矮的柱子的下标之差再减 1,即 5 - 1 - 1)。

class Solution {
public:int largestRectangleArea(vector<int>& heights) {int n = heights.size();vector<int> left(n, -1);vector<int> right(n, n);
​stack<int> st;for (int i = n - 1; i >= 0; --i){while (!st.empty() && heights[i] < heights[st.top()]){left[st.top()] = i;st.pop();}st.push(i);}st = stack<int>();for (int i = 0; i < n; ++i){while (!st.empty() && heights[i] < heights[st.top()]){right[st.top()] = i;st.pop();}st.push(i);}
​int maxArea = 0;for (int i = 0; i < n; ++i){int area = heights[i] * (right[i] - left[i] - 1);if (area > maxArea)maxArea = area;}return maxArea;} 
};

这种解法的时间复杂度是 O(n),空间复杂度也是 O(n)

 


面试题 40 : 矩阵中的最大矩形

题目

请在一个由 0、1 组成的矩阵中找出最大的只包含 1 的矩形并输出它的面积。例如,在下图的矩阵中,最大的只包含 1 的矩形如阴影部分所示,它的面积是 6。

分析

面试题 2.4 是关于最大矩形的,这个题目还是关于最大矩形的,它们之间有没有某种联系?如果能从矩阵中找出直方图,那么就能通过计算直方图中的最大矩形面积来计算矩阵中的最大矩形面积

直方图是由排列在同一基线上的相邻柱子组成的图形。由于题目要求矩形中只包含数字 1,因此将矩阵中上下相邻的值为 1 的格子看成直方图中的柱子。如果分别以上图中的矩阵的每行为基线,就可以得到 4 个由数字 1 的格子组成的直方图,如下图所示。

在将矩阵转换成多个直方图之后,就可以计算并比较每个直方图的最大矩形面积,所有直方图中的最大矩形就是整个矩阵中的最大矩形

代码实现

class Solution {
private:int largestRectangleArea(vector<int>& heights) {int n = heights.size();vector<int> left(n, -1);vector<int> right(n, n);
​stack<int> st;for (int i = n - 1; i >= 0; --i){while (!st.empty() && heights[i] < heights[st.top()]){left[st.top()] = i;st.pop();}st.push(i);}st = stack<int>();for (int i = 0; i < n; ++i){while (!st.empty() && heights[i] < heights[st.top()]){right[st.top()] = i;st.pop();}st.push(i);}
​int maxArea = 0;for (int i = 0; i < n; ++i){int area = heights[i] * (right[i] - left[i] - 1);if (area > maxArea)maxArea = area;}return maxArea;}public:int maximalRectangle(vector<string>& matrix) {if (matrix.size() == 0 || matrix[0].size() == 0)return 0;
​int result = 0;vector<int> heights(matrix[0].size(), 0);for (int i = 0; i < matrix.size(); ++i){for (int j = 0; j < matrix[i].size(); ++j){if (matrix[i][j] == '0')heights[j] = 0;else++heights[j];}
​int maxArea = largestRectangleArea(heights);if (maxArea > result)result = maxArea;}return result;}
};

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

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

相关文章

力扣[面试题 01.02. 判定是否互为字符重排(哈希表,位图)

Problem: 面试题 01.02. 判定是否互为字符重排 文章目录 题目描述思路复杂度Code 题目描述 思路 思路1&#xff1a;哈希表 1.若两个字符串长度不相等&#xff0c;则一定不符合题意&#xff1b; 2.创建一个map集合&#xff0c;先将字符串s1中的每一个字符与其对应的数量存入集合…

【书生·浦语大模型实战营】学习笔记1

大模型成为发展通用人工智能的重要途经 专用模型&#xff1a;针对特定任务&#xff0c;一个模型解决一个问题 通用大模型&#xff1a;一个模型应对多种任务、多种模态 书生浦语大模型系列 上海人工智能实验室 轻量级、中量级、重量级 7B 和 123B的轻量级和中量级大模型都是开源…

Python爬虫——请求库安装

目录 1.打开Anaconda Prompt 创建环境2.安装resuests3.验证是否安装成功4.安装Selenium5.安装ChromeDriver5.1获取chrom的版本5.1.1点击浏览器右上三个点5.1.2点击设置5.1.3下拉菜单&#xff0c;点击最后关于Chrome&#xff0c;获得其版本 5.2 打开网址 [chromedriver](https:/…

树与二叉树---数据结构

树作为一种逻辑结构&#xff0c;同时也是一种分层结构&#xff0c;具有以下两个特点&#xff1a; 1&#xff09;树的根结点没有前驱&#xff0c;除根结点外的所有结点有 且只有一个前驱。 2&#xff09;树中所有结点可以有零个或多个后继。 树结点数据结构 满二叉树和完全二…

GPIO结构

GPIO简介 GPIO(General Purpose Input Output)通用输入输出口 可配置为8种输入输出模式 引脚电平&#xff1a;0V~3.3V,部分引脚可容忍5V 输出模式下可控制端口输出高低电平,用以驱动LED、控制蜂鸣器、模拟通信协议输出时序等 输入模式下可读取端口的高低电平或电压&#x…

推荐系统|物品冷启动01_优化目标评价(包括基尼系数)

文章目录 物品冷启动冷启动的类型“新”按常规推送链路的角度按产品生态角度 物品冷启动的目标和评价指标作者侧用户侧 冷启动的衡量 物品冷启动 冷启动的类型 冷启动的内容种类包括很多方面&#xff0c;本文只介绍UGC的冷启动。 所谓UGC&#xff0c;就是User Generate Conte…

Flink cdc debug调试动态变更表结构

文章目录 前言调试流程1. 拉取代码本地打包2. 配置启动参数3. 日志配置4. 启动验证5. 断点验证 问题1. Cannot find factory with identifier "mysql" in the classpath.2.JsonFactory异常3. NoSuchMethodError异常其他 结尾 前言 接着上一篇Flink cdc3.0动态变更表…

tkinter-TinUI-xml实战(10)展示画廊

tkinter-TinUI-xml实战&#xff08;10&#xff09;展示画廊 引言声明文件结构核心代码主界面统一展示控件控件展示界面单一展示已有展示多类展示 最终效果在这里插入图片描述 ![](https://img-blog.csdnimg.cn/direct/286fcaa2fa5648a992a0ac79b4efad82.png) ………… 结语 引言…

【华为云】容灾方案两地三中心实践理论

应用上云之后&#xff0c;如何进行数据可靠性以及业务连续性的保障是非常关键的&#xff0c;通过华为云云上两地三中心方案了解相关方案认证地址&#xff1a;https://connect.huaweicloud.com/courses/learn/course-v1:HuaweiXCBUCNXI057Self-paced/about当前内容为灾备常见理论…

C++:理解拷贝在变量,指针,引用以及构造函数里的意义

变量&#xff0c;指针&#xff0c;引用 //拷贝与拷贝构造函数 //拷贝&#xff08;copy&#xff09;&#xff1a;拷贝数据&#xff0c;拷贝内存 //始终是在拷贝值&#xff0c;但是指针存储的是内存的地址&#xff0c;变量存储的是数据的值 //特别注意&#xff0c;在引用里面的拷…

Mysql Day04

mysql体系结构 连接层服务层引擎层&#xff08;索引&#xff09;存储层 存储引擎 存储引擎是基于表建立的&#xff0c;默认是innoDB show create table tb; 查看当前数据库支持的存储引擎 show engines; InnoDB 特点 DML&#xff08;数据增删改&#xff09;遵循ACID模…

【算法与数据结构】42、LeetCode接雨水

文章目录 一、题目二、解法三、完整代码 所有的LeetCode题解索引&#xff0c;可以看这篇文章——【算法和数据结构】LeetCode题解。 一、题目 二、解法 思路分析&#xff1a;   程序如下&#xff1a; 复杂度分析&#xff1a; 时间复杂度&#xff1a; O ( ) O() O()。空间复…

Modelsim10.4安装

简介&#xff08;了解&#xff0c;可跳过&#xff09; modelsim是Mentor公司开发的优秀的HDL语言仿真软件。 它能提供友好的仿真环境&#xff0c;采用单内核支持VHDL和Verilog混合仿真的仿真器。它采用直接优化的编译技术、Tcl/Tk技术和单一内核仿真技术&#xff0c;编译仿真速…

AI-TestOps —— 软件测试工程师的一把利剑

写在前面软件测试的前世今生测试工具开始盛行AI-TestOps 云平台● AI-TestOps 功能模块● AI-TestOps 自动化测试流程 写在前面 最近偶然间看到一句话&#xff1a;“软件测试是整个 IT 行业中最差的岗位”。这顿时激起了我对软件测试领域的兴趣&#xff0c;虽然之前未涉及过软…

fast.ai 机器学习笔记(二)

机器学习 1&#xff1a;第 5 课 原文&#xff1a;medium.com/hiromi_suenaga/machine-learning-1-lesson-5-df45f0c99618 译者&#xff1a;飞龙 协议&#xff1a;CC BY-NC-SA 4.0 来自机器学习课程的个人笔记。随着我继续复习课程以“真正”理解它&#xff0c;这些笔记将继续更…

ASUS华硕灵耀X双屏UX8402V工厂模式原厂Win11.22H2系统安装包,含WinRE恢复出厂时开箱状态自带预装OEM系统

适用型号&#xff1a;UX8402VV、UX8402VU 链接&#xff1a;https://pan.baidu.com/s/1D7tJshKTNFYO4YyzKX0ppQ?pwd3saf 提取码&#xff1a;3saf Zenbook Pro灵耀X笔记本电脑原装出厂Windows11系统 带有ASUS RECOVERY恢复功能、自带面部识别&#xff0c;声卡&#xff0c;网…

【RabbitMQ(二)】:Exchange 详解 | Message Convert 消息转换器

文章目录 03. 使用 Java 代码去操控 RabbitMQ3.1 快速入门3.1.1 创建父子项目3.1.2 编写代码 3.2 Work 模型3.3 RabbitMQ 中的三类交换机3.3.1 Fanout 扇出交换机3.3.2 Direct 交换机3.3.3 Topic 交换机 3.4 声明队列交换机3.4.1 方式一&#xff1a;书写 Config 类3.4.2 方式二…

webgis后端安卓系统部署攻略

目录 前言 一、将后端项目编译ARM64 二、安卓手机安装termux 1.更换为国内源 2.安装ssh远程访问 3.安装文件远程访问 三、安装postgis数据库 1.安装数据库 2.数据库配置 3.数据导入 四、后端项目部署 五、自启动设置 总结 前言 因为之前一直做的H5APP开发&#xf…

LLM之RAG实战(二十五)| 使用LlamaIndex和BM25重排序实践

本文&#xff0c;我们将研究高级RAG方法的中的重排序优化方法以及其与普通RAG相比的关键差异。 一、什么是RAG&#xff1f; 检索增强生成&#xff08;RAG&#xff09;是一种复杂的自然语言处理方法&#xff0c;它包括两个不同的步骤&#xff1a;信息检索和生成语言建模。这种方…

爬虫2—用爬虫爬取壁纸(想爬多少张爬多少张)

先看效果图&#xff1a; 我这个是爬了三页的壁纸60张。 上代码了。 import requests import re import os from bs4 import BeautifulSoupcount0 img_path "./壁纸图片/"#指定保存地址 if not os.path.exists(img_path):os.mkdir(img_path) headers{ "User-Ag…