之前收藏了极客时间的算法训练营3期 共10周,计划每周内容分3p博客来记录学习,主要形式为
方法类型1
题1
题解
题2
题解
方法类型2
题1
题解
……
题目大体来自leetcode 和 acwing
主要记录和理解代码,所以基本完全搬运了视频题解代码,
个人学习感受体现在大致思路的总结和注释上。
一、单调栈
思路
矩形面积为底*高,维护一个单调站,栈顶为矩形左墙壁的高值,当遇到打破单调的数据时,开始出栈,出栈时,用最大的左墙壁那个矩形来更新答案,注意,这个左墙壁会随着一个个元素的入栈越来越厚(相当于出栈的时候是砍掉了高的部分。),其实核心就是左墙壁这个矩形。
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;
};