代码随想录训练营第60天|LeetCode 84.柱状图中最大的矩形

news2024/11/20 4:24:55

LeetCode 84.柱状图中最大的矩形

双指针

注意,双指针解法可行,但是在力扣上提交会超时。

以heights[i]为中心,用两个指针向两边扩散,直到heights[left]和heights[right]小于heights[i]为止,这样就构成了以left和right为边界,heights[i]为高的矩形,遍历heights[],取所有矩形中面积的最大值。

代码实现如下:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int result = 0;
        for(int i = 0; i < heights.size(); i++){
            int left = i, right = i;
            for(; left >= 0; left--)    
                if(heights[left] < heights[i])  break;
            for(; right < heights.size(); right++)
                if(heights[right] < heights[i]) break;
            int tmp = heights[i] * (right - left - 1);
            result = max(result, tmp);
        }
        return result;
    }
};

动态规划

从上面的双指针解法中可以发现,每次计算之前都需要往左右两边查找,在动态规划中,先记录每个位置左右两边高度更低的柱子,然后在进行计算。

  1. 确定dp数组下标及其含义
    dp[i][0]:比heights[i]高度更低的左边的柱子下标为dp[i][0]。
    dp[i][1]:比heights[i]高度更低的右边的柱子下标为dp[i][1]。

  2. 确定递推公式
    因为这里要找的是左边和右边第一个更低的柱子,因此不能直接从相邻的柱子得到答案,必须循环遍历,但是相邻的柱子能够提供信息以减少遍历次数。以寻找右边第一个更低的柱子为例,如果右边第一个柱子比当前柱子低,那么直接找到答案;如果右边第一个柱子比当前柱子高,那么下一次比较位置卡以从dp[i+1][1]开始,因为dp[i+1][1]指向的就是第i+1个柱子右边第一个更低的柱子,当前的第i个柱子比第i+1个柱子低,那么从i+2开始到dp[i+1][1]的柱子一定比第i个柱子高,因此可以跳过这些柱子的比较,以提高效率。当前,前提是要先记录右边柱子的dp数组中的值,因此对于dp[i][1],要从右往左遍历。同理,dp[i][0]要从左往右遍历。
    求dp数组的代码实现如下:

dp[0][0] = -1;   //这里的初始化和矩形宽度计算相关,w = right - left - 1
dp.back()[1] = heights.size(); //这里的初始化和矩形宽度计算相关,w = right - left - 1
//求dp[i][0]
for(int i = 1; i < dp.size(); i++){
    int j = i - 1;
    while(j >= 0 && heights[j] >= heights[i]) j = dp[j][0];  //这样更新j比j--效率更高
    dp[i][0] = j;
}
//求dp[i][1]
for(int i = dp.size() - 2; i >= 0; i--){
    int j = i + 1;
    while(j < dp.size() && heights[j] >= heights[i])    j = dp[j][1];
    dp[i][1] = j;
}
  1. 初始化dp数组
    初始化已经在上面的代码中给出,dp[0][0]初始化为-1,一方面在下面的while循环中不会出现死循环,另一方面是要符合宽度计算,宽度w = right - left - 1,其中的left和right指向左右第一个比当前柱子低的柱子。
    dp.back()[1] = heights.size()也是同样的原因。

  2. 确定遍历顺序
    在确定递推公式的分析中就已经得出,dp[i][0]要从前往后遍历,dp[i][1]要从后往前遍历。

  3. 举例推导dp数组
    在这里插入图片描述
    完整的代码实现如下:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int result = 0;
        vector<vector<int>> dp(heights.size(), vector<int>(2));
        dp[0][0] = -1;   //这里的初始化和矩形宽度计算相关,w = right - left - 1
        dp.back()[1] = heights.size(); //这里的初始化和矩形宽度计算相关,w = right - left - 1
        //求dp[i][0]
        for(int i = 1; i < dp.size(); i++){
            int j = i - 1;
            while(j >= 0 && heights[j] >= heights[i]) j = dp[j][0];  //这样更新j比j--效率更高
            dp[i][0] = j;
        }
        //求dp[i][1]
        for(int i = dp.size() - 2; i >= 0; i--){
            int j = i + 1;
            while(j < dp.size() && heights[j] >= heights[i])    j = dp[j][1];
            dp[i][1] = j;
        }
        //求面积
        for(int i = 0; i < dp.size(); i++){
            int tmp = heights[i] * (dp[i][1] - dp[i][0] - 1);
            result = max(result, tmp);
        }
        return result;
    }
};

单调栈解法

注意几个点:
(1)栈里元素从栈顶到栈底递减,即栈顶元素最大。注意,代码实现里栈中存放的是元素下标。
(2)以栈顶元素作为矩形的高
(3)宽等于当前元素下标减去从栈顶开始的第二个元素
(4)根据矩形宽的计算方式,每次计算时栈中至少有两个元素。为了处理边界(即第一个和最后一个元素),需要在原来数组的开头和最后插入一个值为0的元素,这样就不需要单独处理第一个和最后一个元素了。

当前元素和栈顶元素的大小关系有以下的三种可能:

  • 当前元素小于栈顶元素,说明可以以栈顶元素为高,用当前元素和从栈顶开始的第二个元素的下标计算宽度,这样就可以计算面积了。
    在这里插入图片描述
    如图中所示,高为5,宽为2对应的下标减去1对应的下标,这样就可以计算面积了。计算完之后,弹出栈顶元素,再次比较,即当前元素和新的栈顶元素进行比较,如果还是当前元素大,则继续计算,否则将当前元素入栈。
  • 当前元素等于栈顶元素,栈顶元素可以弹栈也可以不弹栈,但是当前元素必须入栈,如下图所示,图中灰色的柱子只能以左边距离最近的更低的柱子为左边界,即时两个柱子高度相同,但只能取最近的一个。栈中有两个高度相同的柱子只会重复计算一次,但不会导致错误。
    在这里插入图片描述
  • 当前元素大于栈顶元素,则直接将当前元素入栈。

完整的代码实现如下:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        stack<int> stk;
        int result = 0;
        stk.push(0);
        heights.insert(heights.begin(), 0);
        heights.push_back(0);
        for(int i = 1; i < heights.size(); i++){
            while(heights[i] < heights[stk.top()]){
                int h = heights[stk.top()];
                stk.pop();
                int w = i - stk.top() - 1;
                result = max(result, w * h);
            }
            stk.push(i);
        }
        return result;
    }
};

上面的代码对于当前元素和栈顶元素相同的情况没有弹出栈顶元素。

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

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

相关文章

第11章_数据库的设计规范(理论了解)

第11章_数据库的设计规范 范式 2.3键和相关属性的概念 范式的定义会使用到主键和候选键&#xff0c;数据库中的键(Key)由一个或者多个属性组成。数据表中常用的几种键和属性的定义: 超键︰能唯─标识元组的属性集叫做超键。候选键︰如果超键不包括多余的属性&#xff0c;那…

基于HOG+LBP完成特征工程,基于机器学习模型同时完成人脸识别+表情识别

这周前两天有时间我写了一篇博文&#xff0c;基于LBP和HOG实现人脸好表情特征的提取计算&#xff0c;之后分别训练了人脸识别模型和表情识别模型&#xff0c;在推理阶段实现了单张图像一次性人脸识别和表情识别的计算分析&#xff0c;但这个我前面就说了这个还是间接的实现方式…

关于GC原理和性能调优实践,看这一篇就够了

前言 本文介绍 GC 基础原理和理论&#xff0c;GC 调优方法思路和方法&#xff0c;基于 Hotspot jdk1.8&#xff0c;学习之后你将了解如何对生产系统出现的 GC 问题进行排查解决。 正文 本文的内容主要如下&#xff1a; GC 基础原理&#xff0c;涉及调优目标&#xff0c;GC 事…

Redis原理篇—数据结构

Redis原理篇—数据结构 笔记整理自 b站_黑马程序员Redis入门到实战教程 底层数据结构 动态字符串SDS 我们都知道 Redis 中保存的 Key 是字符串&#xff0c;value 往往是字符串或者字符串的集合。可见字符串是 Redis 中最常用的一种数据结构。 不过 Redis 没有直接使用C语言中…

Python圣诞树

目录 一、前言 二、创意名 三、效果展示 四、实现步骤 五、编码实现 一、前言 一年一度的圣诞节又要来喽~在这么浪漫的节日里怎么能少的了一个浪漫的程序员呢~让我们一起画个圣诞树&#xff0c;送给你喜欢的那个人吧~ 二、创意名 Python浪漫圣诞树&#xff0c;具体源码见&…

嘿ChatGPT,来帮我写代码

最近 ChatGPT 发行了&#xff0c;这是由 OpenAI 开发的AI聊天机器人&#xff0c;专门研究对话。它的目标是使AI系统更自然地与之互动&#xff0c;但是在编写代码时也可以为您提供帮助。您可以让 ChatGPT 做你的编程助理&#xff0c;甚至更多&#xff01;在过去的几天里&#xf…

腾讯云轻量应用服务器使用 SRS 应用镜像搭建个人直播间、视频转播、本地录制!

SRS 是一个开源的流媒体集群&#xff0c;主要应用在直播和 WebRTC&#xff0c;支持 RTMP、WebRTC、HLS、HTTP-FLV 和 SRT 等常用协议。 轻量应用服务器提供了 SRS 应用镜像&#xff0c;使您无需再关注繁杂的部署操作&#xff0c;即可通过该镜像在轻量应用服务器上一键搭建个人…

安卓/华为手机恢复出厂设置后如何恢复照片

绝大多数安卓用户都会经历过手机恢复出厂设置&#xff0c;部分用户可能没有意识到手机恢复出厂设置可能会导致数据丢失。但是&#xff0c;当您在 云盘上进行备份或在设备上进行本地备份时&#xff0c;情况就会有所不同&#xff0c;并且当您将 安卓手机恢复出厂设置时&#xff0…

LeetCode HOT 100 —— 581. 最短无序连续子数组

题目 给你一个整数数组 nums &#xff0c;你需要找出一个 连续子数组 &#xff0c;如果对这个子数组进行升序排序&#xff0c;那么整个数组都会变为升序排序。 请你找出符合题意的 最短 子数组&#xff0c;并输出它的长度。 思路 方法一&#xff1a;双指针 排序 最终目的是让…

大气湍流自适应光学校正算法matlab仿真,包括涡旋光束,大气湍流影响,不同轨道角动量OAM态之间的串扰,校正等

目录 1.算法描述 2.仿真效果预览 3.MATLAB核心程序 4.完整MATLAB 1.算法描述 涡旋光束是一种具有螺旋波前的光束&#xff0c;在涡旋光束中&#xff0c;决定涡旋光束特性的角量子数可以是任意一个自然数&#xff0c;其不同设置所产生的涡旋光束之间存在正交关系。目前&#…

Android NDK 中堆栈日志 add2line 的分析实践

文章目录目的常用的辅助工具分析步骤参考目的 Android NDK 中出现的 crash 日志分析定位&#xff0c;使用 addr2line 对库中定位so 动态库崩溃位置&#xff0c;定位到某个函数的具体的代码行。 常用的辅助工具 add2line&#xff0c;objdump&#xff0c;ndkstack 等等。本文主要…

一文深度揭开Redis的磁盘持久化机制

前言 Redis 是内存数据库&#xff0c;数据都是存储在内存中&#xff0c;为了避免进程退出导致数据的永久丢失&#xff0c;需要定期将 Redis 中的数据以数据或命令的形式从内存保存到本地磁盘。当下次 Redis 重启时&#xff0c;利用持久化文件进行数据恢复。Redis 提供了 RDB 和…

在linux上安装并初始化配置MariaDB支持远程登录

在linux上安装并初始化配置MariaDB支持远程登录一、环境准备二、启动MariaDB三、初始化MariaDB四、配置远程访问五、补充一些额外的MySql用户赋权限的语句一、环境准备 本文环境是Redhat7上自带的MariaDB, 在安装redhat系统时已经自动安装好了&#xff0c;如果需要自行安装的话…

Selenium 常用函数总结

Seleninum作为自动化测试的工具&#xff0c;自然是提供了很多自动化操作的函数&#xff0c; 下面列举下个人觉得比较常用的函数&#xff0c;更多可见官方文档&#xff1a; 官方API文档&#xff1a; http://seleniumhq.github.io/selenium/docs/api/py/api.html 1) 定位元素 f…

【源码共读】axios的46个工具函数

所有工具函数 还是老样子&#xff0c;先看看axios的工具函数有哪些&#xff0c;先心里有个印象&#xff0c;然后再逐个分析。 直接拉到最下面&#xff0c;可以看到axios的工具函数都是统一导出的&#xff1a; export default {isArray, // 判断是否是数组isArrayBuffer, // …

[机缘参悟-95] :不同人生和社会问题的本质

事情的本质是物极必反&#xff08;轮回、周期&#xff09; 社会的本质是优胜劣汰&#xff08;迭代、发展&#xff09; 道德的本质是伦理秩序&#xff08;未定、秩序&#xff09; 战争的本质是资源占用&#xff08;弱肉、强食&#xff09; 商业的本质是价值交换 金钱的本质…

同事这样用Redis,把我害惨了

首先说下问题现象&#xff1a;内网sandbox环境API持续1周出现应用卡死&#xff0c;所有api无响应现象 刚开始当测试抱怨环境响应慢的时候 &#xff0c;我们重启一下应用&#xff0c;应用恢复正常&#xff0c;于是没做处理。但是后来问题出现频率越来越频繁&#xff0c;越来越多…

MySQL实现主从复制(Windows)的明细操作步骤

文章目录一、教学视频地址二、设计思路三、具体步骤一、教学视频地址 视频地址&#xff1a;视频链接 二、设计思路 准备两个5.7版本的MySQL&#xff0c;一个用作主数据库&#xff0c;另一个用作从数据库。 把主数据库做为写入数据库&#xff0c;从数据库作为读数据库。 三…

【云原生 Kubernetes】基于 KubeAdmin 搭建k8s集群

一、前言 在上一篇&#xff0c;我们基于minikube搭建了一个单节点的k8s集群&#xff0c;作为学习和练习使用的话问题不大&#xff0c;但如果想深入学习和了解k8s的相关技术体系&#xff0c;还是需要搭建真正的集群才能更接近生产环境的应用&#xff0c;本篇将基于KubeAdmin&…

深度学习炼丹-数据预处理和增强

前言一&#xff0c;Normalization 概述 1.1&#xff0c;Normalization 定义1.2&#xff0c;什么情况需要 Normalization1.3&#xff0c;Data Normalization 方法1.4&#xff0c;示例代码 二&#xff0c;normalize images 2.1&#xff0c;图像 normalization 定义2.2&#xff0c…