代码随想录算法训练营Day60 | 84. 柱状图中最大的矩形

文章目录

  • 84. 柱状图中最大的矩形
    • 首尾加 0
    • 双指针

84. 柱状图中最大的矩形

题目链接 | 解题思路

本题和接雨水的题目相互呼应,但是难度略有提升,同样是一道非常棒的题!

在接雨水中,需要找到每一列的左侧最大值和右侧最大值;在本题中,需要找到每一列的左侧第一个更小值和右侧第一个更小值。这个要求的变化加大了双指针的难度,所以优先讨论更加适用的单调栈解法。

和上一题一样按行来计算矩形面积,对于固定的一列,需要这一列的高度、对应的左边界、对应的右边界。但这一题,单调栈的储存顺序发生了变化。

在这里插入图片描述

  1. 单调栈内的元素顺序、如何取值
    • 栈内元素应该是 top-bottom 递减的,此时向右搜索,找到当前元素大于栈口元素的时候就代表着找到了栈口元素这一列代表的高度的右边界
    • 当前元素大于栈口元素时:
      • 右边界即为当前元素
      • 基准位置即为栈口元素
      • 左边界即是栈口元素的内部相邻元素
  2. 当前元素与栈口元素相等:和接雨水的处理一样
    • 之所以能够对元素值相等的情况有多种处理,是因为在计算中只需要他们代表的值来作为基准高度,而不需要他们的确切下标,利用的下标是左边界和右边界。

对于当前元素与栈口元素的三种比较情况,也和接雨水中的方法无异。

首尾加 0

本题最大的不同就在于需要在输入数组的首尾各加入一个 0,从而解决一些特殊的输入数组:

  • 单调递增数组:heights = [2, 4, 6, 8]

    • 对这样的输入,每一步循环都会将当前下标压入栈。直到遍历结束,也不会有执行计算面积的操作,因为没有碰到一个更小的元素。所以在尾部加入一个 0,可以确保在这种情况下执行正确的面积计算。
  • 单调递减数组:heights = [8, 6, 4, 2]

    • 对这样的输入,每一步循环都会触发面积计算。然而,如上所述,面积的计算需要当前基准高度、左边界、右边界。对第二个下标 1 执行面积计算时,得到右边界 1,基准下标 0。弹出 0 后已经得到了一个空栈,无法得到左边界。所以,这种情况下也不会执行计算面积,因为总是不能得到左边界。
    • 所以在首部加入一个 0,可以确保在这种情况下执行正确的面积计算。

可以看到,在接雨水中我们没有特殊考虑过这两种情况。因为在接雨水中,如果不能够形成一个完整的谷,那么就不需要进行任何计算,也就是说单调递增、递减的数组本来就不该有任何计算结果。而在本题,任何输入都应该正确计算面积,所以不能只计算完整的峰,而要全面考虑。

class Solution:def largestRectangleArea(self, heights: List[int]) -> int:heights.insert(0, 0)heights.append(0)result = 0stack = [0]for i in range(1, len(heights)):if heights[i] > heights[stack[-1]]:stack.append(i)elif heights[i] == heights[stack[-1]]:stack.pop()stack.append(i)else:while (len(stack) > 0 and heights[i] < heights[stack[-1]]):mid = stack[-1]stack.pop()if len(stack) > 0:left = stack[-1]curr_width = i - left - 1curr_height = heights[mid]result = max(result, curr_height * curr_width)stack.append(i)return result

双指针

和接雨水一样,本题同样有双指针 + dp 的解法,但是这个解法的时间复杂度有些可疑。
和接雨水相比,本题中的双指针 + dp 会更加复杂一些,因为要求的不再是最大值而是第一个更小值的下标,dp 的递推变得很困难,我也不确定具体的复杂度。

这题的 dp 部分有点像是 KMP,感觉很有意思。

class Solution:def largestRectangleArea(self, heights: List[int]) -> int:# records the index of the first smaller value on the left of ileft_smaller_idx = [0] * len(heights) left_smaller_idx[0] = -1            # no smaller value on the left, representing non-existencefor i in range(1, len(heights)):temp = i - 1while (temp >= 0 and heights[temp] >= heights[i]):temp = left_smaller_idx[temp]left_smaller_idx[i] = temp# records the index of the first smaller value on the right of iright_smaller_idx = [0] * len(heights) right_smaller_idx[-1] = len(heights)            # no smaller value on the right, representing non-existencefor i in range(len(heights) - 2, -1, -1):temp = i + 1while (temp < len(heights) and heights[temp] >= heights[i]):temp = right_smaller_idx[temp]right_smaller_idx[i] = tempresult = 0for i in range(len(heights)):result = max(result, heights[i] * (right_smaller_idx[i] - left_smaller_idx[i] - 1))return result

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

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

相关文章

设计模式之访问器模式(Visitor)的C++实现

1、访问器模式的提出 在软件开发过程中&#xff0c;早已发布的软件版本&#xff0c;由于需求的变化&#xff0c;需要给某个类层次结构增加新的方法。如果在该基类和子类中都添加新的行为方法&#xff0c;将给代码原有的结构带来破坏&#xff0c;同时&#xff0c;也违反了修改封…

无涯教程-JavaScript - ACOTH函数

描述 ACOTH函数返回数字的反双曲余切。 语法 ACOTH (number)争论 Argument描述Required/OptionalNumberThe absolute value of Number must be greater than 1. i.e., Number must be must be less than -1 or greater than 1.Required Notes 用于计算双曲反余切的方程为-…

软件设计师考试学习1

前言 计算机组成原理及体系结构 数据的表示 进制的转换 原码反码补码移码 最高位是符号位&#xff0c;负数符号位为1 反码补码正数和原码一样&#xff0c;负数有区别 反码符号位不动&#xff0c;其他位置取反 补码在反码基础上加1 移码是将补码的符号为取反 在原码和反码中…

谷歌翻译API接口,翻译API接口,翻译API接口申请指南

Google翻译API是一种可以在多个平台上使用的Web服务&#xff0c;通过使用该API&#xff0c;用户可以将任何文本转换成多种语言&#xff0c;同时也可以将多种语言转换成用户指定的语言。目前Google翻译API支持超过100种语言&#xff0c;涵盖了全球范围内的所有主流语言。 Googl…

数据结构基础8:二叉树oj+层序遍历。

二叉树oj层序遍历 题目一&#xff1a;二叉树的销毁&#xff1a;方法一&#xff1a;前序遍历&#xff1a;方法二&#xff1a;后序遍历&#xff1a; 题目二&#xff1a;二叉树查找值为x的节点方法一&#xff1a;方法二&#xff1a;方法三&#xff1a; 题目三&#xff1a;层序遍历…

二叉树(堆)

堆的性质&#xff1a; 堆中某个节点的值总是不大于或不小于其父节点的值&#xff1b; 堆总是一棵完全二叉树。 大堆&#xff1a;任何父亲≥孩子 小堆&#xff1a;任何父亲≤孩子 接下来&#xff0c;我们要做的便是对堆进行增加和删除&#xff1a; 首先是增加操作&#xff0c…

leetcode 673. 最长递增子序列的个数

2023.9.13 做本题之前先复习一下 最长递增子序列 &#xff0c;在此基础上还要加个count数组&#xff0c;用于记录当前下标 最长递增子序列的个数。 本题还是有些难度&#xff0c;日后再来二刷。

每日一题~中序后序遍历构造二叉树

原题链接&#xff1a;106. 从中序与后序遍历序列构造二叉树 - 力扣&#xff08;LeetCode&#xff09; 题目描述&#xff1a; 思路分析&#xff1a; 后序遍历分析图 中序遍历分析图 不难看出后序遍历的结果中的最后一个元素就是根节点&#xff0c;倒数第二个元素则是根节点的…

Electron和vue3集成(推荐仅用于开发)

本篇我们仅实现Electron和vue3通过先运行起vue3项目&#xff0c;再将vue3的url地址交由Electron打开的方案&#xff0c;仅由Electron在vue3项目上套一层壳来达到脱离本机浏览器运行目的 1、参考快速上手 | Vue.js搭建起vue3初始项目 npm install -g vue npm install -g vue/c…

深入理解Linux网络笔记(一):内核是如何接收网络包的

本文为《深入理解Linux网络》学习笔记&#xff0c;使用的Linux源码版本是3.10&#xff0c;网卡驱动是Intel的igb网卡驱动 Linux源码在线阅读&#xff1a;https://elixir.bootlin.com/linux/v3.10/source 1、内核是如何接收网络包的 1&#xff09;、Linux网络收包总览 在TCP/I…

手刻 Deep Learning -第壹章 -PyTorch教学-激励函数与感知机入门(上)

一、前言 本文接续前篇教学 Pytorch 与线性回归 &#xff0c;本文着重在 Activation Function &#xff08; 中文称 激励函数 &#xff09;&#xff0c;我们会介绍激励函数 &#xff08;也有人称 激活函数&#xff1f; 激发函数&#xff1f; &#xff09; 为什么会有用&#xf…

vim,emacs,verilog-mode这几个到底是啥关系?

vim&#xff1a;不多说了被各类coder誉为地表最强最好用的编辑器&#xff1b;gvim&#xff0c;gui vim的意思&#xff1b; emacs&#xff1a;也是一个编辑器&#xff0c;类似vscode&#xff1b; vim在使用的时候为了增强其功能&#xff0c;有好多好多插件&#xff0c;都是以.…