笔记1 第一周 p3 单调栈,单调队列——柱状图中最大的矩形,接雨水,——极客时间算法训练营

之前收藏了极客时间的算法训练营3期 共10周,计划每周内容分3p博客来记录学习,主要形式为

方法类型1

题1

题解

题2

题解

方法类型2

题1

题解

……

题目大体来自leetcode 和 acwing

主要记录和理解代码,所以基本完全搬运了视频题解代码,

个人学习感受体现在大致思路的总结和注释上。


一、单调栈

1.柱状图中最大的矩形

思路

矩形面积为底*高,维护一个单调站,栈顶为矩形左墙壁的高值,当遇到打破单调的数据时,开始出栈,出栈时,用最大的左墙壁那个矩形来更新答案,注意,这个左墙壁会随着一个个元素的入栈越来越厚(相当于出栈的时候是砍掉了高的部分。),其实核心就是左墙壁这个矩形。

class Solution {
public:
    int largestRectangleArea(vector<int>& heights) {
        heights.push_back(0);//避免了只有一个数值只入栈不更新答案的问题。
        int ans = 0;
        for(int height : heights){
            int accumulatedWidth = 0;//要开始出栈,开始计算出了多少宽度
            while(!s.empty() && height< s.top().height){//维护单调性,先判断栈内是否符合要求,本题为,栈维护了可以构成矩形的最大左边,所以在遍历到每个点时,之前更高的边要出栈。
                accumulatedWidth += s.top().width;//要注意栈顶的宽度是累计的宽度。
                ans = max(ans,accumulatedWidth * s.top().height);//每次单调性被打破时会更新答案
                s.pop();//已经使用过这个值来更新答案,没有价值了。
            }
            s.push({accumulatedWidth + 1, height});//单调性被打破时才更新答案
//总底边宽度加上1,这个左边的墙被削去了高的块,底留下了。
        }
        return ans;
    }
private:
    struct Rect{
        int width;
        int height;
    };
    stack<Rect> s;

};

2.接雨水

解法1,单调栈(类似求最大矩形,横着求)

class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        for (int h : height) {
            int accumulatedWidth = 0;
            while (!s.empty() && h > s.top().height) {
                accumulatedWidth += s.top().width;
                int bottom = s.top().height;
                s.pop();//需要用到栈的第二个元素
                if (s.empty()) continue;//左边没有了,直接溜走。
                ans +=  accumulatedWidth * (min(s.top().height, h) - bottom);//两边都得够高
            }
            s.push({accumulatedWidth + 1, h});
        }
        return ans;
    }
private:
    struct rect {
        int width;
        int height;
    };
    stack<rect> s;
};

解法2,前后缀最大值求单个柱子(竖着求)

思路

只考虑每一个格子自己能留住多少水,前面后面都有比较高的柱子,能留住水,

class Solution {
public:
    int trap(vector<int>& height) {
        int ans = 0;
        int n = height.size();
        preMax = vector<int>(n); //之后最高的柱子
        sufMax = vector<int>(n);//之前最高的柱子
        preMax[0] = height[0];
        for(int i = 1; i < n; i++){
            preMax[i] = max(preMax[i-1],height[i]);//动态规划
        }
        sufMax[n-1] = height[n-1];
        for(int i = n-2; i >= 0; i--){
            sufMax[i] = max(sufMax[i+1],height[i]);
        }
        for(int i = 1; i < n; i++){//只考虑每个点自己能接多少
            int bottom = height[i];
            int up = min(preMax[i], sufMax[i]);
            if(up > bottom) ans += up - bottom;
        }
        return ans;
    }
private:
    vector<int> preMax;//前后缀最大。
    vector<int> sufMax;
};

二、单调队列

1.滑动窗口最大值

思路

维护最大的有用数值。

class Solution {
public:
    vector<int> maxSlidingWindow(vector<int>& nums, int k) {
        for(int i = 0; i < nums.size(); i++){
            //队头为想要的答案,队尾为可能能用的答案
            //先去掉队头过期的
            while(!q.empty() && q.front() <= i - k) q.pop_front();
            //新来的肯定存在时间长,要是队尾没有新来的大,就没用。
            while(!q.empty() && nums[i] >= nums[q.back()]) q.pop_back();
            q.push_back(i);
            if(i >= k - 1) ans.push_back(nums[q.front()]);
        }
        return ans;
    }
private:
    deque<int> q; //双向队列存储了有效答案的下标。
    vector<int> ans;

};
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值