【八十五】【算法分析与设计】单调栈的全新版本,两个循环维护左小于和右小于信息,84. 柱状图中最大的矩形,85. 最大矩形

84. 柱状图中最大的矩形

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

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

示例 1:

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

示例 2:

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

提示:

  • 1 <= heights.length <=10(5)

  • 0 <= heights[i] <= 10(4)

1.

思路就是以每一个位置的柱状图的高度作为矩形的高,然后看以这个高能够扩多长的底,计算这个情况下的面积.

问题被划分为几个小的部分,以每一个高度为高去计算.

2.

单调栈存储左小于,右小于.

老旧的单调栈写法是在元素出栈的时候维护数据.

但是发现这样特别浪费内存,有的时候还需要给栈开辟二维空间处理相同的数据.

这种做法实在是太低效了,并且在这道题目的提交结果居然超出了题目的内存.

3.

全新的版本,只用一个单独的栈就可以完成重复元素或者不重复元素.

首先我们需要确定我们的目的是什么,是填写信息每一个元素左小于,右小于的信息.

老旧版本是在出栈的时候维护信息,全新版本是在入栈的时候维护信息.

每一个元素入栈的时候,维护栈内单调性,然后维护入栈元素左小于或者右小于信息.

对于每一个入栈元素,栈内一定是单调性,此时一定可以找到左小于或者右小于.

老旧版本

 
class Solution {
public:
    vector<int> arr;
    int n;
    int ret;
    using p = pair<int, int>;
    vector<p> p_m;
    vector<vector<int>> st;

    void init() {
        n = arr.size();
        p_m.clear(), p_m.resize(n);
    }
    void solve() {
        int i = 0;
        while (1) {
            if (i >= n)
                break;
            if (st.empty())
                st.push_back({i});
            else {
                auto top = st.back();
                while (1) {
                    if (arr[i] >= arr[top[0]])
                        break;
                    st.pop_back();
                    for (auto& x : top) {
                        p_m[x].second = i;
                        p_m[x].first = st.empty() ? -1 : st.back().back();
                    }
                    if (st.empty())
                        break;
                    else
                        top = st.back();
                }
                if (st.empty())
                    st.push_back({i});
                else {
                    if (arr[st.back()[0]] == arr[i])
                        st.back().push_back(i);
                    else
                        st.push_back({i});
                }
            }
            i++;
        }

        while(1){
            if(st.empty())break;
            auto top=st.back();
            st.pop_back();
            for(auto& x:top){
                p_m[x].second=n;
                p_m[x].first=st.empty()?-1:st.back().back();
            }
        }

        for(int i=0;i<n;i++){
            ret=max(ret,arr[i]*(p_m[i].second-p_m[i].first-1));
        }
    }

    int largestRectangleArea(vector<int>& _heights) {
        arr = _heights;
        init(), solve();
        return ret;
    }
};

全新版本

 
class Solution {
public:
    vector<int> arr;
    //下标-元素
    int n;
    //元素的个数
    int ret;
    //记录结果信息
    using p = pair<int, int>;
    vector<p> p_m;
    //记录每一个元素左小于,右小于的信息
    vector<int> st;
    //vector模拟栈
    void init() {
        n = arr.size();
        p_m.clear(), p_m.resize(n);
    }
    void solve() {
    //首先维护每一个元素的左小于的信息
        for (int i = 0; i < n; i++) {
            while (1) {
            //维护栈内单调性,严格的小到大
                if (st.empty())
                    break;
                if (arr[st.back()] < arr[i])
                    break;
                st.pop_back();
                //出栈,因为此时他们都不可能再作为某元素的左小于了.
            }
            //维护入栈元素的左小于信息
            p_m[i].first = st.empty() ? -1 : st.back();
            st.push_back(i);
            //入栈
        }
        
        st.clear();
        //栈清空
        
        //维护所有元素的右小于信息
        //此时必须是从后往前填写,因为对于j位置我需要找右小于,那么右边所有元素需要入栈并且栈保持单调
        for (int i = n - 1; i >= 0; i--) {
            while (1) {
                if (st.empty())
                    break;
                if (arr[st.back()] < arr[i])
                    break;
                st.pop_back();
            }
            p_m[i].second = st.empty() ? n : st.back();
            //维护右小于信息
            st.push_back(i);
        }

        for (int i = 0; i < n; i++) {
            ret = max(ret, arr[i] * (p_m[i].second - p_m[i].first - 1));
        }
    }

    int largestRectangleArea(vector<int>& _heights) {
        arr = _heights;
        init(), solve();
        return ret;
    }
};

85. 最大矩形

给定一个仅包含 01 、大小为 rows x cols 的二维二进制矩阵,找出只包含 1 的最大矩形,并返回其面积。

示例 1:

 
 

class Solution { public: vector<int> arr; //下标-元素 int n; //元素的个数 int ret; //记录结果信息 using p = pair<int, int>; vector<p> p_m; //记录每一个元素左小于,右小于的信息 vector<int> st; //vector模拟栈 void init() { n = arr.size(); p_m.clear(), p_m.resize(n); } void solve() { //首先维护每一个元素的左小于的信息 for (int i = 0; i < n; i++) { while (1) { //维护栈内单调性,严格的小到大 if (st.empty()) break; if (arr[st.back()] < arr[i]) break; st.pop_back(); //出栈,因为此时他们都不可能再作为某元素的左小于了. } //维护入栈元素的左小于信息 p_m[i].first = st.empty() ? -1 : st.back(); st.push_back(i); //入栈 } st.clear(); //栈清空 //维护所有元素的右小于信息 //此时必须是从后往前填写,因为对于j位置我需要找右小于,那么右边所有元素需要入栈并且栈保持单调 for (int i = n - 1; i >= 0; i--) { while (1) { if (st.empty()) break; if (arr[st.back()] < arr[i]) break; st.pop_back(); } p_m[i].second = st.empty() ? n : st.back(); //维护右小于信息 st.push_back(i); } for (int i = 0; i < n; i++) { ret = max(ret, arr[i] * (p_m[i].second - p_m[i].first - 1)); } } int largestRectangleArea(vector<int>& _heights) { arr = _heights; init(), solve(); return ret; } };

输入:matrix = [["1","0","1","0","0"],["1","0","1","1","1"],["1","1","1","1","1"],["1","0","0","1","0"]] 输出:6 解释:最大矩形如上图所示。

示例 2:

输入:matrix = [["0"]] 输出:0

示例 3:

输入:matrix = [["1"]] 输出:1

提示:

  • rows == matrix.length

  • cols == matrix[0].length

  • 1 <= row, cols <= 200

  • matrix[i][j]'0''1'

1.

思路是每一行看作是柱状图的开始,那么题目完全转化为84. 柱状图中最大的矩形.

用dp记录每一个位置的柱状图高度.

每一行都计算一次,ret记录所有情况的最大值即可.

2.

入栈的时候维护信息.

一个循环维护左信息,一个循环维护右信息.

 
class Solution {
public:
    vector<vector<char>> arr;
    //arr表示矩形原始数据
    int row, col;
    //row和col表示矩形的行和列数
    int ret;
    //ret记录结果数据
    vector<vector<int>> dp;
    //dp记录所有位置的柱状图高度
    vector<vector<int>> p_min;
    //存储每一行的柱状图的左小于右小于信息
    vector<int> st;
    //模拟栈
    void init() {
        row = arr.size(), col = arr[0].size();
        dp.clear(), dp.resize(row, vector<int>(col));
        //计算dp值
        for (int j = 0; j < col; j++) {
            for (int i = 0; i < row; i++) {
                if (arr[i][j] == '1')
                    dp[i][j] = 1 + (i - 1 >= 0 ? dp[i - 1][j] : 0);
                else
                    dp[i][j] = 0;
            }
        }
        p_min.clear(), p_min.resize(col, { 0, 0 });
        //用{}大括号初始化vector,长度小的时候很方便
        
        for (int i = 0; i < row; i++) {
            for (int j = 0; j < col; j++) {
                cout << dp[i][j] << " ";
            }
            cout << endl;
        }
        //看一下dp值有没有计算计算错误
    }
    void solve() {
    //正式开始解题
        for (int i = 0; i < row; i++) {
        //首先遍历每一行,表示每一个柱状图
            p_min.clear(), p_min.resize(col, { 0, 0 });
            st.clear();
            //初始化左小于右小于信息
            //初始化栈
            
            for (int j = 0; j < col; j++) {
            //先维护当前行的左小于信息
                while (1) {
                    if (st.empty())break;
                    if (dp[i][j] > dp[i][st.back()])break;
                    st.pop_back();
                }
                p_min[j][0] = st.empty() ? -1 : st.back();
                //维护当前j位置左小于信息
                st.push_back(j);
                //入栈
            }
            st.clear();
            //栈清空
            
            //维护右小于信息
            for (int j = col - 1; j >= 0; j--) {
                while (1) {
                    if (st.empty())break;
                    if (dp[i][j] > dp[i][st.back()])break;
                    st.pop_back();
                }
                p_min[j][1] = st.empty() ? col : st.back();
                st.push_back(j);
            }

            //此时完成当前行的左小于右小于信息维护
            //计算此时可能的矩形面积
            for (int j = 0; j < col; j++) {
                ret = max(ret, (p_min[j][1] - p_min[j][0] - 1) * dp[i][j]);
            }

            for (int j = 0; j < col; j++) {
                cout << p_min[j][0] << " " << p_min[j][1] << " ";
            }
            cout << endl;
            //打印看一下有没有问题
        }
    }

    int maximalRectangle(vector<vector<char>>& _matrix) {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        cout.tie(nullptr);
        arr = _matrix;
        init();
        solve();
        return ret;
    }
};

结尾

最后,感谢您阅读我的文章,希望这些内容能够对您有所启发和帮助。如果您有任何问题或想要分享您的观点,请随时在评论区留言。

同时,不要忘记订阅我的博客以获取更多有趣的内容。在未来的文章中,我将继续探讨这个话题的不同方面,为您呈现更多深度和见解。

谢谢您的支持,期待与您在下一篇文章中再次相遇!

  • 14
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

妖精七七_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值