首页 > 其他分享 >力扣-84. 柱状图中最大的矩形

力扣-84. 柱状图中最大的矩形

时间:2024-05-17 20:56:41浏览次数:21  
标签:更小高 right int heights 力扣 柱状图 ans 84 left

1.题目介绍

题目地址(84. 柱状图中最大的矩形 - 力扣(LeetCode))

https://leetcode.cn/problems/largest-rectangle-in-histogram/

题目描述

给定 n 个非负整数,用来表示柱状图中各个柱子的高度。每个柱子彼此相邻,且宽度为 1 。

求在该柱状图中,能够勾勒出来的矩形的最大面积。

 

示例 1:

输入:heights = [2,1,5,6,2,3]
输出:10
解释:最大的矩形为图中红色区域,面积为 10

示例 2:

输入: heights = [2,4]
输出: 4

 

提示:

  • 1 <= heights.length <=105
  • 0 <= heights[i] <= 104

2.题解

2.1 暴力枚举(枚举左边界-底边)

思路

这里我们枚举左边界和右边界的范围,这样我们也就确定了heights数组的遍历范围,从中根据木桶效应找到最小的高,面积=底边*高

代码

  • 语言支持:C++

C++ Code:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        int ans = 0;
        // 枚举左边界
        for(int left = 0; left < n; left++){
            int h = heights[left];
            for(int right = left; right < n; right++){
                h = min(h, heights[right]);
                ans = max(ans, (right - left + 1) * h);
            }
        }
        return ans;
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(n^2)\)
  • 空间复杂度:\(O(n)\)

2.2 暴力枚举(枚举高)

思路

我们首先笃定矩形的高为某个值,然后以这个高为中心,向两边进行中心扩展,知道遇到小于这个高的情况(这时候再加上这个小于的就与我们笃定的假设相驳)
这里需要注意一下,如果我们使用的是while循环,最后结束的情况是第一个不满足条件的left或right而不是最后一个满足条件的left或right,
所以这里两种处理方法,要不然后面使用left+1和right-1, 结果改为(right - 1 - left - 1 + 1) * height; 要不然while判断条件改为如下代码所示即可

代码

  • 语言支持:C++

C++ Code:

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        int ans = 0;
        // 枚举高
        for(int mid = 0; mid < n; mid++){
            int left = mid, right = mid;
            while(left-1 >= 0 && heights[left-1] >= heights[mid]){
                left--;
            }
            while(right+1 < n && heights[right+1] >= heights[mid]){
                right++;
            }
            ans = max(ans, (right - left + 1) * heights[mid]);
        }
        return ans;
    }
};

复杂度分析

令 n 为数组长度。

  • 时间复杂度:\(O(n^2)\)
  • 空间复杂度:\(O(n)\)

2.3 单调栈

思路

这里我们继续2.2中确定高的思路,从中间向两边扩展,我们要找到左边第一个小于该高的索引和右边第一个小于该高的索引
如果向2.2一样,我们每次先遍历到每个高,然后向四周进行扩展,这样最坏情况相当于我们每次都需要遍历一次整个heights数组
有没有办法我们事先知道每个高它左侧第一个更小高和右侧第一个更小高呢? 这里我们就思考使用单调栈来解决问题.

我们对于该heights数组进行事先处理.求出更小高并存储到两个数组中
首先对于左侧元素,我们从左侧开始遍历,这里有一个性质,如果出现一个高hi小于其左侧的hx,那么对于索引i右侧的所有元素,他们找更小高,只有可能找到hi或者更左侧的高,而不会找到hx,
因为 x < i < ..., 如果 hi < h... 那么就是hi; 如果 hi > h, 又因为 hi < hx, 所以 hx > hi > h..., 必然不可能是hx

我们考虑用一个单调栈来维护这些高(帮助我们快速找到更小高),
1.如果当前高小于等于栈顶高,说明从此处以后,该栈顶高不可能作为更小高存在,我们不需要进行判断,将其从栈中弹出即可;
2.如果当前高大于栈顶高,说明我们找到了最近更小高(由于栈先进后出的性质,栈顶的是我们最近压进去的),将其进行存储
3.将当前高进行存储,压入栈(整体栈还是单调栈)

注意一点,这里我们找到了最近更小高的索引,但是我们实际需要的不是最近更小高的索引(第一个不满足条件的高), 而是要的最后一个满足条件的高(边界)
即实际上是left + 1 和 right - 1, 但这里我们存的是 left 和 right, 所以最后(right - 1 - (left + 1) + 1) = (right - left - 1)为实际的底边
但是我们也可以在存储动动手脚, 改为'left[i] = stk.empty()? 0:stk.top() + 1;' 和 right[j] = stk.empty()? n - 1 :stk.top() - 1; 找到的就是边界了!

代码

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        int n = heights.size();
        stack<int> stk;
        vector<int> left(n), right(n);
        for(int i = 0; i < n; i++){
            int h = heights[i];
            while(!stk.empty() && h <= heights[stk.top()]){
                stk.pop();
            }
            left[i] = stk.empty()?-1:stk.top();
            stk.push(i);
        }

        stk = stack<int>();
        for(int j = n - 1; j >= 0; j--){
            int h = heights[j];
            while(!stk.empty() && h <= heights[stk.top()]){
                stk.pop();
            }
            right[j] = stk.empty()?n:stk.top();
            stk.push(j);
        }

        int ans = 0;
        for(int i = 0; i < n; i++){
            ans = max(ans, (right[i] - left[i] - 1) * heights[i]);
        }
        return ans;
    }
};

标签:更小高,right,int,heights,力扣,柱状图,ans,84,left
From: https://www.cnblogs.com/trmbh12/p/18197926

相关文章

  • 力扣-96. 下一个更大元素 I
    1.题目题目地址(496.下一个更大元素I-力扣(LeetCode))https://leetcode.cn/problems/next-greater-element-i/题目描述nums1 中数字 x 的下一个更大元素是指 x 在 nums2中对应位置右侧的第一个比 x 大的元素。给你两个没有重复元素的数组 nums1和 nums......
  • TB8845 如何解析为恒流驱动芯片
    1、产品概述TB8845是一款外围电路简单的多功能平均电流型LED恒流驱动器,适用于4-60V电压范围的降压BUCK大功率调光恒流LED领域。芯片PWM端口支持超小占空比的PWM调光,可响应最小60ns脉宽。芯片137采用我司138专利14191算法,为客户提供最佳解决方案,最大限度地发挥灯具......
  • CF1884D Counting Rhyme 题解
    题目链接:CF或者洛谷给个莫反题解,讲讲常规套路题目要求满足没有\(a_k\mida_i与a_k\mida_j\)的\((i,j)\)的对数,显然即不存在\(a_k\mid\gcd(a_i,a_j)\)。稍微拓展下,如果不存在整除多个数,那么显然不整除它们的\(\gcd\)即可,因为它们的公因数即为满足的最大数,如果为......
  • POI 重叠、并列柱状图(条形图),显示数据,自定义颜色
    1、pom.xml<dependency><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId><version>4.1.2</version></dependency><dependency><groupId>org.apache.poi</groupId><artifac......
  • hdu2084数塔
    思路:从顶层走到底层可以变为从底层走到顶层,dp[i][j]=a[i][j]+max(dp[i+1][j],dp[i+1][j+1])importjava.util.Scanner;publicclasshdu2084{publicstaticvoidmain(String[]args){//TODO自动生成的方法存根Scannersc=newScanner(System.......
  • 力扣224.基本计算器(困难)
    题目​ 给你一个字符串表达式s,请你实现一个基本计算器来计算并返回它的值。解题思路我们可以使用两个栈nums和ops。nums:存放所有的数字ops:存放所有的数字以外的操作,+/-也看做是一种操作然后从前往后做,对遍历到的字符做分情况讨论:空格:跳过(:直接加入ops......
  • [LeetCode] 1584.连接所有点的最小费用 Kruskal And Prim 算法 Java 并查集
    Problem:1584.连接所有点的最小费用目录Kruskal算法复杂度CodePrim算法复杂度CodeKruskal算法复杂度时间复杂度:添加时间复杂度,示例:$O(mlog(m))$空间复杂度:添加空间复杂度,示例:$O(n)$CodeclassSolution{publicintminCostConnectPoints(int[][]po......
  • 力扣第130场双周赛
    判断矩阵是否满足条件给定二维矩阵,判断所有格子是否满足如下条件:如果它下面的格子存在,那么它需要等于它下面的格子如果它右边的格子存在,那么它需要不等于它右边的格子遍历二维矩阵,简单模拟即可。classSolution{public:boolsatisfiesConditions(vector<vector<in......
  • 力扣1553 2024.5.13
    原题网址:此处为链接个人难度评价:1700分析:虽然不知道为什么贪心不对,但总之贪心不对。数据如此大也难以DP,那么只有搜索了。显然有一眼可得的搜索记忆化:记忆当只剩下k个果时还需要几天。值得一提的是,本代码实际上可能并不是一个正解代码,其可能无法在整数域上保证所有答案正确,但在......
  • 力扣-232. 用栈实现队列
    1.题目信息2.题解2.1双栈的使用(用栈实现队列)思路我们一开始可能会想到对于每次入栈操作——由于我们可能希望他是加在队尾(栈底)而不是队头(栈首),所以我们就进行一次首尾互换,将instack中的数据倒腾到outstack,由于栈先进后出的特性,所以这时候原来的栈底在头部,我们直接将元素pus......