zoukankan      html  css  js  c++  java
  • LeetCode 单调栈专题

    单调栈

    单调栈应用场景及模板

    应用场景:“找最接近某个元素”的最值问题
    如:查找每个数左边第一个比它小的数;
    再如:查找每个数右边第一个比它大的数;等等

    洛谷P5788 【模板】单调栈
    题意:找每个数右侧第一个大于它的数
    两种写法:正序或者倒序;

    正序:从0~n-1,对于当前元素i,每次弹出已在栈中的(位于i左侧)比i小的元素;那么这些被弹出的元素右侧第一个大于它的数就是元素i了;即出栈的时候记录答案;
    最终栈中永远只保持单调递减,因为比当前元素i小的都弹出了,剩下的左侧都是比i大的数

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    int n;
    const int maxn = 3e6+100;
    ll a[maxn];
    int stk[maxn];
    ll result[maxn];
    int top = 0;
    
    
    
    /*
    正序 找右边第一个大的数
    单调栈中总保持递减  小的都出栈 它们的答案被标记为当前元素 
    */
    
    
    int main() {
    	scanf("%d",&n);
    	for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
    	for(int i=1;i<=n;i++){
    		while(top>0 && a[stk[top]] < a[i]){ //注意是小于 不是 大于等于 
    			result[stk[top]] = i;
    			top--;
    		}
    		stk[++top] = i;
    	}
    	for(int i=1;i<=n;i++) printf("%d ",result[i]);
    	return 0;
    }
    /*
    5
    1 4 2 3 5
    */
    

    倒序:从n-1到1;找右边第一个大的数 ;把从元素i右侧(已经在栈中)小的都出栈;直到遇到栈中第一个比i对应a[i]大的数;小的都出栈后,最后栈顶就对应了i右侧第一个比它大的数。需要判断栈顶是不是为空。

    #include<bits/stdc++.h>
    using namespace std;
    
    typedef long long ll;
    int n;
    const int maxn = 3e6+100;
    ll a[maxn];
    stack<int> stk;
    ll result[maxn];
    
    /*
    找右边第一个大的数 
    从后往前 小的都出栈 直到遇到栈中第一个比i对应a[i]大的
    就是向右边看的第一个最大 
    */
    
    
    int main() {
    	ios::sync_with_stdio(false);
    	cin>>n;
    	for(int i=1;i<=n;i++) cin>>a[i];
    	for(int i=n;i>=1;i--){
    		while(!stk.empty() && a[stk.top()] <= a[i]){
    			stk.pop();
    		}
    		if(stk.empty()) result[i] = 0;
    		else result[i] = stk.top();
    		stk.push(i);
    	}
    	for(int i=1;i<=n;i++) cout<<result[i]<<" ";
    	return 0;
    }
    /*
    5
    1 4 2 3 5
    */
    

    739. 每日温度

    找右边第一个比当前大的元素位置,与当前i的距离

    即单调栈中是单调递减序列

    (进来一个新元素i小的都被弹出栈,出栈的过程中小的最大值答案都是当前位置i)

    这种情况下要考虑栈中剩余的元素

    倒序从右向左,

    class Solution {
    public:
        stack<int> stk;
        vector<int> result;
        int n;
        vector<int> dailyTemperatures(vector<int>& T) {
            n = T.size();
            result = vector<int>(n);
            for(int i=n-1;i>=0;i--){
            	//右边小于等于它的都出栈 剩下的肯定都是大于它的
                while(!stk.empty() 
    				&& T[stk.top()] <= T[i]) stk.pop();
    			//特判栈顶是不是空
    			//若不空,当前i对应的答案就是栈顶(第一个大的)
                if(stk.empty()) result[i] = 0;
                else result[i] = stk.top() - i;
                stk.push(i);
            }
            return result;
        }
    };
    

    正序从左向右:

    class Solution {
    public:
        stack<int> stk;
        vector<int> result;
        int n;
        vector<int> dailyTemperatures(vector<int>& T) {
            n = T.size();
            result = vector<int>(n);
            for(int i=0;i<n;i++){
            	//左边(已在栈中)小于当前元素的都出栈 当前i就是它们的答案
                while(!stk.empty() && T[stk.top()] < T[i]) {
                    result[stk.top()] = i - stk.top();
                    stk.pop();
                }
                //剩下的都是大于等于当前元素 栈中总保持单调递减
                stk.push(i);
            }
            while(!stk.empty()){
                result[stk.top()] = 0;
                stk.pop();
            }
            return result;
        }
    };
    

    84. 柱状图中最大的矩形

    枚举位置,用这个位置的上边界数值作为对象

    1.找到左边最近的第一个比当前值小的位置下标

    2.找到右边最近的第一个比当前值小的位置下标

    算出宽度,边界作为长;面积就是 长 × 宽

    用两遍单调栈维护,从左边开始比它小的;再维护一遍从右边开始比它小的

    class Solution {
    public:
        int n;
        int ans;
        stack<int> stk;
        vector<int> left,right;
        //1.从左向右找第一个比它小的
        //2.从右向左找第一个比它小的
        int largestRectangleArea(vector<int>& heights) {
            n = heights.size();
            left = vector<int>(n),right = vector<int>(n);
            ans = 0;
            for(int i=0;i<n;i++){
                //左边(已经在栈中) 大于等于i的都出栈 
                //最终栈首它左边留下第一个小于它的
                while(!stk.empty() && 
                	heights[stk.top()] >= heights[i]){
                    stk.pop();
                }
                if(stk.empty()) left[i] = -1;
                else left[i] = stk.top();
                stk.push(i);
            }
            while(stk.size()) stk.pop();
            for(int i=n-1;i>=0;i--){
                //右边(已经在栈中) 大于等于当前i的都出栈 
                //最终栈首留下右边第一个小于它的
                while(!stk.empty() && 
                	heights[stk.top()] >= heights[i]){
                    stk.pop();
                }
                if(stk.empty()) right[i] = n;
                else right[i] = stk.top();
                stk.push(i);
            }
            for(int i=0;i<n;i++) 
                ans = max(ans,((right[i] - 1) - (left[i] + 1) + 1 ) * heights[i]);
            return ans;
        }
    };
    

    只使用一次单调栈的题解,没看明白为什么只用一次

    42. 接雨水

    可以这样来思考:新来一个柱子增加的面积是多少?就是它与左侧第一个比它大的柱子之间的这段空间的面积。

    那么这段空间的面积如何计算?可以从当前柱子开始依次向左考虑增加的面积;

    左边遇到一个比它高度小的,增加的面积就是 (高-上一个比他小的) * 宽度,对应上图橙色区域

    即新来一个柱子,移除左边比它小的柱子,直到找左侧比它大的柱子;

    每次移除小柱子时,计算与前一个小柱子的高度差作为长;距离新柱子的距离作为宽。新增加面积就是长×宽

    1.一层一层加面积,找到左边第一个比它大的元素。

    在线更新:在单调栈出栈更新的时候(即左边柱子比它小得时候)的过程中加面积

    2.最后再加上自己水平到比他大的一个矩形

    class Solution {
    public:
        int n;
        stack<int> stk;
        //新来一个柱子i  找它左边第一个比它大的柱子   其它小的柱子在出栈的过程中更新
        int trap(vector<int>& height) {
            n = height.size();
            int ans = 0;
            for(int i=0;i<n;i++){
                int lastHeight = 0;
                //把左侧比当前i高度小的都出栈
                //栈的过程中更新last高度 计算这次面积
                while(!stk.empty() && 
                	height[stk.top()] <= height[i]){
                	//求宽高  计算柱子i与它左侧top柱子的面积
                    int weight = i - stk.top() - 1; 
                    int high = height[stk.top()] - lastHeight; 
                    ans += high * weight;
                    lastHeight = height[stk.top()];
                    stk.pop();
                }
                if(!stk.empty()){ //加上自己与左侧第一个比它高的面积
                    ans += (height[i]-lastHeight) * 
                    (i - stk.top() - 1);
                }
                stk.push(i);
            }
            return ans;
        }
    };
    
  • 相关阅读:
    【20170923】2017暑假北京学习 day 3
    Leetcode 39
    事无巨细 | 访问一个网站的过程
    Leetcode 77
    排序 | 快速排序
    排序 | 堆排序
    Leetcode 60
    Leetcode 51
    海量数据处理的解法
    Leetcode 99
  • 原文地址:https://www.cnblogs.com/fisherss/p/12978871.html
Copyright © 2011-2022 走看看