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


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

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

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

    解析:
    在查看所有可能的最大矩形后:发现最大矩形中

    • 有长的、短的、相等的综合起来的矩形条
    • 就是一个矩形条

    其结果很明显是:短的矩形条然后向左右延伸不包括其左边第一个小于该柱子的矩形条,不包括其右边第一个大于该柱子的矩形条,所形成的的矩形。

    • 因此只需要遍历所有柱子,然后左右寻找到左边第一个小于该柱子的矩形条,边第一个大于该柱子的矩形条。然后求值即可。

    代码如下:超时

    class Solution {
    public:
        int largestRectangleArea(vector<int>& heights) {
            int result = 0;
            for(int i = 0; i < heights.size(); i++){
                int left, right;
                left = right = i;
                for(int l = i-1; l >= 0; --l){
                    if(heights[l] < heights[i]) break;
                    left = l;
                }
                for(int r = i+1; r < heights.size(); ++r){
                    if(heights[r] < heights[i]) break;
                    right = r;
                }
                if(result < (right-left+1)*heights[i]) result =  (right-left+1)*heights[i];
            }
            return result;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    动态规划解法:
    这个可以过,原因在于:求当前值左边更大值的下标可以通过dp数组对应的前面一个值来取巧,而并不需要从当前值向左遍历所有情况来取得当前值左边更大值的下标。取得当前值右边更大值的下标同理。

    代码:

    class Solution {
    public:
        int largestRectangleArea(vector<int>& heights) {
            vector<int> minLeftIndex(heights.size());
            vector<int> minRightIndex(heights.size());
            int size = heights.size();
    
            // 记录每个柱子 左边第一个小于该柱子的下标
            minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
            for (int i = 1; i < size; i++) {
                int t = i - 1;
                // 这里不是用if,而是不断向左寻找的过程
                while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
                minLeftIndex[i] = t;
            }
            // 记录每个柱子 右边第一个小于该柱子的下标
            minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
            for (int i = size - 2; i >= 0; i--) {
                int t = i + 1;
                // 这里不是用if,而是不断向右寻找的过程
                while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
                minRightIndex[i] = t;
            }
            // 求和
            int result = 0;
            for (int i = 0; i < size; i++) {
                int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
                result = max(sum, result);
            }
            return result;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32

    单调栈:
    本题是要找每个柱子左右两边第一个小于该柱子的柱子,因此当遍历到某值小于栈顶时,栈顶对应的柱体的最大面积就找到了。因此单调栈里的顺序是从小到大。
    栈顶和栈顶的下一个元素以及要入栈的三个元素组成了我们要求最大面积的高度和宽度。

    剩下就是分析清楚如下三种情况:

    • 情况一:当前遍历的元素heights[i]小于栈顶元素heights[st.top()]的情况
    • 情况二:当前遍历的元素heights[i]等于栈顶元素heights[st.top()]的情况
    • 情况三:当前遍历的元素heights[i]大于栈顶元素heights[st.top()]的情况

    代码如下:

    需要注意的是当当前元素等于栈顶元素的时候,栈顶元素可以删除可以不删除,因为这两个柱体的最大面积是一样的,只保留一个也可,两个都计算也可。

    class Solution {
    public:
        int largestRectangleArea(vector<int>& heights) {
            stack<int> st;
            heights.insert(heights.begin(), 0); // 数组头部加入元素0
            heights.push_back(0); // 数组尾部加入元素0
            st.push(0);
            int result = 0;
            // 第一个元素已经入栈,从下标1开始
            for (int i = 1; i < heights.size(); i++) {
                // 注意heights[i] 是和heights[st.top()] 比较 ,st.top()是下标
                if (heights[i] > heights[st.top()]) {
                    st.push(i);
                } else if (heights[i] == heights[st.top()]) {
                    st.pop(); // 这个可以加,可以不加,效果一样,思路不同
                    st.push(i);
                } else {
                    while (heights[i] < heights[st.top()]) { // 注意是while
                        int mid = st.top();
                        st.pop();
                        int left = st.top();
                        int right = i;
                        int w = right - left - 1;
                        int h = heights[mid];
                        result = max(result, w * h);
                    }
                    st.push(i);
                }
            }
            return result;
        }
    };
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33

    因为这三种情况都有push操作,所以可以化简:如下

    class Solution {
    public:
        int largestRectangleArea(vector<int>& heights) {
            stack<int> st;
            heights.insert(heights.begin(), 0); // 数组头部加入元素0
            heights.push_back(0); // 数组尾部加入元素0
            st.push(0);
            int result = 0;
            for (int i = 1; i < heights.size(); i++) {
                while (heights[i] < heights[st.top()]) {
                    int mid = st.top();
                    st.pop();
                    int w = i - st.top() - 1;
                    int h = heights[mid];
                    result = max(result, w * h);
                }
                st.push(i);
            }
            return result;
        }
    };
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
  • 相关阅读:
    CAD二次开发--像纬地与CASS程序一样双击桌面图标实现插件的自动挂载(不用netload也不用进入后输入挂载命令)
    电子器件系列37:SD卡座(Push-Push和Push-Pull)
    C#根据excel文件中的表头创建数据库表
    【算法模板】快速排序模板
    在云上使用 OpenText 实现业务关键型应用程序的现代化
    走近Harvest Moon:Moonbeam DeFi狂欢会
    【从零开始学习 SystemVerilog】2.11、SystemVerilog 数据类型—— Array Manipulation(数组操作)
    使用余弦算法计算向量相似性
    FPGA语法相关知识合集
    卓越领先!安全狗入选2023年福建省互联网综合实力50强
  • 原文地址:https://blog.csdn.net/qq_56762247/article/details/126364419