84. 柱状图中最大的矩形(单调栈)

题目链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台

 解题思路:

方法一:暴力解法

矩形的面积由宽和高决定,可以枚举所有的高度,也就是固定高度,然后从当前高度所在的位置向两边走,分别找到左边和右边第一个小于柱子高度的位置left和right,那么right-left-1就是所求的宽度。

代码实现如下:

class Solution {
    public int largestRectangleArea(int[] heights) {
        if (heights == null || heights.length == 0) {
            return 0;
        }
        int result = 0;
        for (int i = 0; i < heights.length; i++) {
            int height = heights[i];
            int left = i-1;
            int right = i+1;
            while (left >= 0 && heights[left] >= height) {
                left--;
            }
            while (right < heights.length && heights[right] >= height) {
                right++;
            }
            int width = right - left - 1;
            result = Math.max(result, width * height);

        }
        return result;
    }
}

 上述代码的时间复杂度为O(n^2),超时了

方法二:双指针数组优化

  1. 方法一中,每一次枚举高度的时候都需要向两边寻找宽度,没有将有用的信息记录下来,在方法一种最关键的就是找到当前高度位置左右两边小于当前柱子高度的下标,每次枚举当前高度时,都需要重新找,所以可以使用两个指针数组,将当前位置左右两边小于当前柱子高度的下标记录下来,减少寻找宽度的次数
  2. 两个指针数组: 数组长度  n = heights.length
    1. minLeftIndex[ i ]:表示当前柱子 i  左边第一个小于该柱子高度的下标
      1. 初始值:minLeftIndex[0] = -1
      2. 从位置1开始从左往右求解 minLeftInde数组中的每一个值:
        1. t = i -1
        2. 如果 t >=0 && heights[ t ] >= heights[ i ],循环执行:
          1. t = minLeftIndex[ t ]:因为minLeftIndex数组是从左往右计算的,所以对于 minLeftIndex [ i ]的求解,不需要依次向左寻找小于当前柱子的下标。如果 t 位置的柱子高度大于当前柱子高度,而是利用已经求解的minLeftIndex数组,直接找到小于 t 位置柱子高度的下标 minLeftIndex [ t ],然后在判断 minLeftIndex [ t ] 位置的柱子高度是否小于当前柱子高度,如果不小于,继续该过程。可见这种方式减少了很多寻找过程
        3. minLefIndex[ i ] = t
    2. minRightIndex[ i ]:表示当前柱子 i 右边第一个小于该柱子高度的小标
      1. 初始值:minRightIndex[ n-1 ] = n
      2. 从位置 n-2 开始从右往左求解数组中的每一个值:
        1. t = i +1
        2. 如果 t < heights.length && height[ t ] >= height[ i ],循环执行:
          1. t = minRightIndex[ t ]:这里的思想和求解minLeftIndex数组类似
        3. minRightIndex[ i ] = t;
  3. 之后就可以利用已经求解的两个指针数组计算宽度,位置 i 上的柱子对应的宽度为:  width = minRightIndex[i] -minLeftIndex[i]-1

AC代码:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int[] minLeftIndex = new int[heights.length];
        int[] minRightIndex = new int[heights.length];
        
        minLeftIndex[0] = -1;
        for (int i = 1; i < heights.length; i++) {
            int t = i - 1;
            while (t >= 0 && heights[t] >= heights[i]) {
                t = minLeftIndex[t];
            }
            minLeftIndex[i] = t;
        }

        minRightIndex[heights.length - 1] = heights.length;
        for (int i = heights.length - 2; i >= 0; i--) {
            int t = i + 1;
            while (t < heights.length && heights[t] >= heights[i]) {
                t = minRightIndex[t];
            }
            minRightIndex[i] = t;
        }

        int sum = 0;
        for (int i =0;i<heights.length;i++){
            int currentWidth = minRightIndex[i]-minLeftIndex[i]-1;
            int currentHeight = heights[i];
            sum=Math.max(sum,currentHeight*currentWidth);
        }
        return sum;
    }
}

 方法三:单调栈(首先是一个栈,并且从栈底到栈顶的元素有序)

  1. 单调栈内的元素满足:从栈底到栈顶元素是升序的(单调的含义,栈中元素单调有序)
  2. 单调栈适合这种场景:一维数组中要寻找任意一个元素的左边或者右边第一个比自己大或者小的元素的位置

在方法二中,双数组优化时,需要寻找左右两边第一个比当前柱子高度小的柱子的下标,所以可以使用单调栈来元素下标。

  1. 确定单调栈中元素的顺序:因为找到是左右两边第一个比当前柱子高度小的柱子的下标,所以从栈底到栈顶的元素是升序的,即栈顶元素最大。
  2. 如果当前元素小于栈顶元素,说明当前元素就是栈顶元素右边第一个高度小于栈顶元素的柱子。栈顶元素下边的那个元素就是栈顶元素左边第一个高度小于栈顶元素的柱子,此时,就可以计算栈顶元素所代表高的最大矩阵面积。计算完成之后,将栈顶元素弹出,当前元素继续和新的栈顶元素比较,如果小于,继续该过程,如果大于,当前元素入栈

特殊情况:

  1. 如果height数组本身就是升序的,那么,当前元素会一直大于栈顶元素,当前元素一直入栈,并不会计算新的矩阵大小,所以可以在数组最右边添加一个数字 0 ,避免这种情况,当出现0时,当前元素0小于栈顶元素,就会一直计算栈中柱子的高所代表的最大矩阵面积了
  2. 如果height数组本身是降序的,那么,当前元素会一直小于栈顶元素,会一直弹出栈顶元素,这个时候,栈就为空了(栈中只会保存一个元素),为了避免这种情况,可以在数组最左边添加一个0

AC代码:

class Solution {
    public int largestRectangleArea(int[] heights) {
        int result = 0;
        ArrayDeque<Integer> stack = new ArrayDeque<>();

        int[] newHeights = new int[heights.length+2];
        System.arraycopy(heights,0,newHeights,1,heights.length);
        newHeights[heights.length+1]=0;
        result = heights[0];
        
        stack.push(0);
        for (int i = 1; i < newHeights.length; i++) {
            //如果当前柱子的高度小于栈顶元素
            while (!stack.isEmpty()&&newHeights[i]<newHeights[stack.peek()]){
                int top = stack.pop();
                if (stack.isEmpty()){
                    break;
                }
                int left = stack.peek();
                int width = i - left-1;
                result = Math.max(result,width*newHeights[top]);
            }
            stack.push(i);
        }
        return result;
    }
}

 

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AD7151

AD7151采用ADI公司的电容-数字转换器&#xff08;CDC&#xff09;技术,这种技术汇集了与实际传感器接口过程中起着重要作用的众多特性于一身,如高输入灵敏度,较高的输入寄生接地电容和泄漏电流容限。 集成自适应式阈值算法可对因环境因素&#xff08;如湿度和温度&#xff09;…

Azure资源命名和标记决策指南

参考 azure创建虚拟机在虚拟机中选择编辑标签&#xff0c;并添加标记&#xff0c;点击应用 3.到主页中转到所有资源 4. 添加筛选器并应用 5.查看结果&#xff0c;筛选根据给服务器定义的标签筛选出结果。 参考链接: https://learn.microsoft.com/zh-cn/azure/cloud-adoption…

BBS项目day02、注册、登录(登录之随机验证码)、修改密码、退出登录、密码加密加盐

一、注册 1.注册之前端页面 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>注册页面</title><!--动态引入文件-->{% load static %}<script src"{% static js/jquery.min.js %…

hbase 报错 Master passed us a different hostname to use; was=

原因 wsl2的 /etc/hosts 配置的不兼容,我这里是ubuntu22 命令行输入hostname 看输出什么,比如输出 aaa 那么替换/etc/hosts 127.0.0.1 aaa

React源码解析18(5)------ 实现函数组件【修改beginWork和completeWork】

摘要 经过之前的几篇文章&#xff0c;我们实现了基本的jsx&#xff0c;在页面渲染的过程。但是如果是通过函数组件写出来的组件&#xff0c;还是不能渲染到页面上的。 所以这一篇&#xff0c;主要是对之前写得方法进行修改&#xff0c;从而能够显示函数组件&#xff0c;所以现…

你的汽车充电桩控制板可能比你的智能手机还要智能?

你是否想过&#xff0c;你的汽车充电桩控制板可能比你的智能手机还要智能?今天我们就来聊聊这个话题。 汽车充电桩控制板的智能性让充电过程更加高效、安全。首先&#xff0c;它具备自检功能&#xff0c;就像你的手机一样&#xff0c;不仅能检查出设备的工作状态&#xff0c;还…

【大数据】Flink 详解(二):核心篇 Ⅲ

Flink 详解&#xff08;二&#xff09;&#xff1a;核心篇 Ⅲ 29、Flink 通过什么实现可靠的容错机制&#xff1f; Flink 使用 轻量级分布式快照&#xff0c;设计检查点&#xff08;checkpoint&#xff09;实现可靠容错。 30、什么是 Checkpoin 检查点&#xff1f; Checkpoint …

概率论与数理统计复习总结2

概率论与数理统计复习总结&#xff0c;仅供笔者复习使用&#xff0c;参考教材&#xff1a; 《概率论与数理统计》/ 荣腾中主编. — 第 2 版. 高等教育出版社《2024高途考研数学——概率基础精讲》王喆 概率论与数理统计实际上是两个互补的分支&#xff1a;概率论 在 已知随机…

关于安卓打包生成aar,jar实现(一)

关于安卓打包生成aar&#xff0c;jar方式 背景 在开发的过程中&#xff0c;主项目引入三方功能的方式有很多&#xff0c;主要是以下几个方面&#xff1a; &#xff08;1&#xff09;直接引入源代码module&#xff08;优点&#xff1a;方便修改源码&#xff0c;易于维护&#…

IPv4分组

4.3.1 IPv4分组 IP协议定义数据传送的基本单元——IP分组及其确切的数据格式 1. IPv4分组的格式 IPv4分组由首部和数据部分&#xff08;TCP、UDP段&#xff09;组成&#xff0c;其中首部分为固定部分&#xff08;20字节&#xff09;和可选字段&#xff08;长度可变&#xff0…

第一百二十四天学习记录:C++提高:STL-deque容器(上)(黑马教学视频)

deque容器 deque容器基本概念 功能&#xff1a; 双端数组&#xff0c;可以对头端进行插入删除操作 deque与vector区别 vector对于头部的插入删除效率低&#xff0c;数据量越大&#xff0c;效率越低 deque相对而言&#xff0c;对头部的插入删除速度比vector快 vector访问元素的…

探索数据之美:初步学习 Python 柱状图绘制

文章目录 一 基础柱状图1.1 创建简单柱状图1.2 反转x和y轴1.3 数值标签在右侧1.4 演示结果 二 基础时间线柱状图2.1 创建时间线2.2 时间线主题设置取值表2.3 演示结果 三 GDP动态柱状图绘制3.1 需求分析3.2 数据文件内容3.3 列表排序方法3.4 参考代码3.5 运行结果 一 基础柱状图…

Lombok的使用及注解含义

文章目录 一、简介二、如何使用2.1、在IDEA中安装Lombok插件2.2、添加maven依赖 三、常用注解3.1、Getter / Setter3.2、ToString3.3、NoArgsConstructor / AllArgsConstructor3.4、EqualsAndHashCode3.5、Data3.6、Value3.7、Accessors3.7.1、Accessors(chain true)3.7.2、Ac…

Redis——String类型详解

概述 Redis中的字符串直接按照二进制的数据存储&#xff0c;不会有任何的编码转换&#xff0c;因此存放什么样&#xff0c;取出来的时候就什么样。而MySQL默认的字符集是拉丁文&#xff0c;如果插入中文就会失败 Redis中的字符串类型不仅可以存放文本数据&#xff0c;还可以存…

实现自己的“妙鸭相机“,十分钟学会roop插件

9.9买不了吃亏,9.9买不了上当&#xff0c;只要9.9就可以拥有属于自己的艺术写真 但是不知道你是否注意到用户协议中 有这一条 "我方在全世界&#xff08;包括元宇宙等虚拟空间&#xff09;范围内享有永久的、不可撤销的、可转让的、可授权的、免费的和非独家的许可&#x…

【日常积累】HTTP和HTTPS的区别

背景 在运维面试中&#xff0c;经常会遇到面试官提问http和https的区别&#xff0c;今天咱们先来简单了解一下。 超文本传输协议HTTP被用于在Web浏览器和网站服务器之间传递信息&#xff0c;HTTP协议以明文方式发送内容&#xff0c;不提供任何方式的数据加密&#xff0c;如果…

16.3.2 【Linux】程序的管理

程序之间是可以互相控制的。举例来说&#xff0c;你可以关闭、重新启动服务器软件&#xff0c;服务器软件本身是个程序&#xff0c; 你既然可以让她关闭或启动&#xff0c;当然就是可以控制该程序。 使用kill-l或者是man 7 signal可以查询到有多少个signal。主要的讯号代号与名…

smardaten实战丨谁说无代码不能开发出漂亮的门户首页?

一、需求背景 门户首页对于一个公司或组织来说是一个极其重要的网站页面&#xff0c;它可以作为访问者了解和获取相关信息的入口&#xff0c;同时也是展示品牌形象和吸引目标受众的重要工具。 开发一个门户首页需要开发团队在向访问者展示关于公司或组织基本信息的基础上&…

使用Edge和chrom扩展工具(GoFullPage)实现整页面截图或生成PDF文件

插件GoFullPage下载&#xff1a;点击免费下载 如果在浏览网页时&#xff0c;有需要整个页面截图或导出PDF文件的需求&#xff0c;这里分享一个Edge浏览器的扩展插件&#xff1a;GoFullPage。 这个工具可以一键实现页面从上到下滚动并截取。 一、打开“管理扩展”&#xff08;…

Linux下在qtcreator中创建qt程序

目录 1、新建项目 2、单工程项目创建 3、多工程项目创建 4、添加子工程&#xff08;基于多工程目录结构&#xff09; 5、 .pro文件 1、新建项目 切换到“编辑”界面&#xff0c;点击菜单栏中的“文件”-“新建文件或项目” 2、单工程项目创建 只有一个工程的项目&#…