单调栈和滑动窗口模板

常见模型:找出每个数左边离它最近的比它大/小的数 https://www.acwing.com/blog/content/404/

单调栈的运用找左边和右边第一个比它大的元素
 

题目1:找到左边比它小的,或者右边比它大的,有两种写法:for循环从大到小,for循环从小到大。

对于找右边的XXX,从N-1~0比较容易

对于找左边的XXX,从0~N-1比较容易

因为这样就不需要处理当没有元素的时候,栈里面还有元素的情况了。 https://www.acwing.com/problem/content/832/

#include<iostream>
#include<vector>
#include<stack>
using namespace std;
int main(){
    int N;
    cin >> N;
    vector<int> m_vec(N);
    for(int i = 0 ; i < N; i++){
        int tmp;
        cin >> tmp; 
        m_vec[i] = tmp;
    }
    
    //单调栈结构
    vector<int> res(N);
    stack<int> m_stack;
    for(int i = 0 ; i < N; i++){
        while(!m_stack.empty() && m_stack.top() >= m_vec[i]){
            m_stack.pop();
        }
        res[i] = m_stack.empty() ? -1 : m_stack.top();
        m_stack.push(m_vec[i]);
    }
    
    for(int i = 0 ; i < res.size(); i++){
        cout << res[i] << " ";
    }
    
}
#include <iostream>
#include <stack>


using namespace std;

const int N = 1e5+10;

int arr[N];
//int st[N];
int n,m;
int main()
{
    cin >> n;
    stack<int> st;
    for(int i= 0;i < n;i++){
        cin >> arr[i];
    }

    for(int i= n-1;i >= 0;i--){
        while(!st.empty() && arr[i] < arr[st.top()] ){
            int idx = st.top();
            arr[idx] = arr[i];
            st.pop();
        }

        st.push(i);
    }

    while(!st.empty()){
        int idx = st.top();
        arr[idx] = -1;
        st.pop();
    }

    for(int i = 0; i < n;i++){
        cout << arr[i] << ' ';
    }

    return 0;
}

作者:defddr
链接:https://www.acwing.com/solution/acwing/content/2248/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

上面那种单调栈只能处理右边第一个比它大的,如果题目想要同时知道左边第一个比它大的以及右边第一个比它大的,应该怎么做呢?后面发现是自己理解错了,实际上结构是一样的。。。。。

题目三连:

84. 柱状图中最大的矩形 https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

这题为了编程的方便,最重要的点就是加了0进去,或则是其他数来充当哑节点。

85. 最大矩形 https://leetcode-cn.com/problems/maximal-rectangle/

利用了上一个题目的思路,看不出来好吗.....https://leetcode-cn.com/problems/maximal-rectangle/

86. 数组中累积和与最小值的乘积, 假设叫做指标A。给定一个数组, 请返回子数组中, 指标A最大的值
 

首先要想到如何将题目进行转换?

要找到左边第一个小的,右边第一个比它小的。

84.

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        //实际上这道题目就转换为寻找左边第一个比它小,右边第一个比它小
        //维护一个递增序列
        stack<int> m_stack;
        m_stack.push(-1);
        int maxArea = 0;
        for(int i =  0 ; i < heights.size(); i++){
            while(m_stack.top()!=-1 && heights[m_stack.top()] > heights[i] ){
                int id= m_stack.top();
                int height = heights[id];
                m_stack.pop();           
                int width = (i - m_stack.top() -1 );
                maxArea = max(maxArea, height * width);
            }
            m_stack.push(i);
        }
        while(m_stack.top()!=-1){
            int id = m_stack.top();
            m_stack.pop();
            int height = heights[id];
            int width = (heights.size() - m_stack.top() - 1); //这里注意是heights.size() 
            maxArea = max(maxArea, height * width);
        }
        return maxArea;
    }
};

 这个做法非常棒,利用了最后一个元素0进行了一次清空的操作.

class Solution {
public:
    /**
     * @param height: A list of integer
     * @return: The area of largest rectangle in the histogram
     */
    int largestRectangleArea(vector<int> &height) {
        // write your code here
        if(height.size() == 0) return 0;
        int res = 0;
        vector<int> tmp = height;
        tmp.push_back(0);  // Important
 
        stack<int> s;    		//维护单调递增
        for(int i = 0; i < tmp.size(); i++)
        {
            if(s.empty() || (!s.empty() && tmp[i] >= tmp[s.top()])) s.push(i);		//如果当前高度大于栈顶,直接压入
            else{
                while(!s.empty() && tmp[s.top()] > tmp[i]) 			//如果栈顶高度大于当前高度
                {
                    int idx = s.top(); s.pop();						//保存栈顶元素信息
                    int width = s.empty() ? i : (i-s.top()-1);		//如果栈已经为空,宽度为i,否则i-s.top()-1
                    res = max(res, tmp[idx] * width);  
                }
                s.push(i);  // Important							//压入栈中
            }
        }
        return res;
    }
};

 85.

利用84题的程序

class Solution {
public:


    int largestRectangleArea(vector<int>& heights) {
        //实际上这道题目就转换为寻找左边第一个比它小,右边第一个比它小
        //维护一个递增序列
        stack<int> m_stack;
        m_stack.push(-1);
        int maxArea = 0;
        for(int i =  0 ; i < heights.size(); i++){
            while(m_stack.top()!=-1 && heights[m_stack.top()] > heights[i] ){
                int id= m_stack.top();
                int height = heights[id];
                m_stack.pop();           
                int width = (i - m_stack.top() -1 );
                maxArea = max(maxArea, height * width);
            }
            m_stack.push(i);
        }
        while(m_stack.top()!=-1){
            int id = m_stack.top();
            m_stack.pop();
            int height = heights[id];
            int width = (heights.size() - m_stack.top() - 1); //这里注意是heights.size() 
            maxArea = max(maxArea, height * width);
        }
        return maxArea;
    }

    int maximalRectangle(vector<vector<char>>& matrix) {
        if(matrix.size() == 0 || matrix[0].size() ==0) return 0;
        vector<int> heights(matrix[0].size());
        int maxArea = 0;
        for(int i = 0 ; i < matrix.size(); i++){
            for(int j = 0 ; j  < matrix[0].size(); j++){
                if(matrix[i][j] == '1'){
                    heights[j]++;
                }else{
                    heights[j] = 0;
                }
            }
            maxArea = max(maxArea, largestRectangleArea(heights));
        }
        return maxArea;
    }
};

滑动窗口题目模板:

下面给出了单调队列的模板,当然是labuladong大佬写得。不过这个模板如果要事先要声明一个单调队列的话,存在一些问题,就是不能存储下标值,这样的话算法的通用性不是很高。

class MonoQueue{
private:
    deque<int> m_queue;
public:
    void push(int n){
        //再遇到比你厉害的人之前一直弹出,所以得到了一个递减序列
        while(!m_queue.empty() && m_queue.back() < n){
            m_queue.pop_back();
        }
         m_queue.push_back(n);
    }

    int max() { return m_queue.front();}

    void pop(int n){
        //按理来说直接pop就可以,为什么需要m_queue.front() 呢?
        //因为m_queue在push的时候可能已经将元素n给删除了
        if(!m_queue.empty() && m_queue.front() == n){
            m_queue.pop_front();
        }
    }

};
class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(nums.size() < k || nums.size() == 0) return vector<int>();
        MonoQueue m_queue;
        vector<int> res;
        for(int i = 0 ; i < nums.size(); i++){
            if(i < k -1){
                m_queue.push(nums[i]);
            }else{
                m_queue.push(nums[i]);
                res.push_back(m_queue.max());
                m_queue.pop(nums[i-k+1]);
            }
        }
        return res;
    }
};

如果将此题改成按标号的话,会发现代码比较复杂,


class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        if(nums.size() < k || nums.size() == 0) return vector<int>();
        deque<int> m_queue;
        vector<int> res;
        for(int i = 0 ; i < nums.size(); i++){
            if(i < k - 1){
                while(!m_queue.empty() && nums[m_queue.back()] < nums[i]){
                    m_queue.pop_back();
                }
                m_queue.push_back(i);
            }else{
                 while(!m_queue.empty() && nums[m_queue.back()] < nums[i]){
                    m_queue.pop_back();
                }
                m_queue.push_back(i);
                res.push_back(nums[m_queue.front()]);

                if( (i-k+1) == m_queue.front()){
                    m_queue.pop_front();
                }               
            }
        }
        return res;
    }
};

那还不如直接以后遇到类似题目就使用下面这个模板呢~~~~

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        vector<int> res;
        deque<int> q;
        for(int i=0;i<nums.size();i++){
            while(q.size()&&q.front()<=i-k)q.pop_front();//当前节点小于窗口最小的边,弹出
            while(q.size()&&nums[q.back()]<=nums[i])q.pop_back();//如果最后的值小于当前的值时,将最后的一个值弹出
            q.push_back(i);//每次迭代更新值

            if(i>=k-1)res.push_back(nums[q.front()]);
        }
        return res;
    }
};

作者:炼心
链接:https://www.acwing.com/blog/content/759/
来源:AcWing
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值