代码随想录第五十八天|单调栈
什么时候使用单调栈
通常是一维数组,要寻找任一个元素的右边或者左边第一个比自己大或者小的元素的位置,此时我们就要想到可以用单调栈了。时间复杂度为O(n)。
单调栈的本质是空间换时间。
Leetcode739. 每日温度
题目链接:Leetcode739. 每日温度
尝试暴力遍历,超时了
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int len=temperatures.size();
vector<int> answer(len,0);
for(int index=0;index<len;index++){
int higher=index+1;
while(higher<len){
if(temperatures[higher]>temperatures[index]){
answer[index]=higher-index;
break;
}
}
}
return answer;
}
};
构建一个单调递减的栈,栈内存入对应数据下标。
这个栈的规则是,维持栈内单调递减,从左往右遍历,来一个比头大的,那肯定是右边首个大于头的,就更新答案数组,再pop掉这个头
总的来说,分三种情况,栈空-当前元素塞进去、栈头>=当前元素-当前元素塞进去、栈头<当前元素-更新answer[栈头]并pop栈头
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
int len=temperatures.size();
if(len==1) return {
0};
vector<int> answer(len,0);
stack<int> st;
for(int i=0;i<len;i++){
while(!st.empty()&&temperatures[i]>temperatures[st.top()]){
answer[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
return answer;
}
};
Leetcode496.下一个更大元素 I
题目链接:Leetcode496.下一个更大元素 I
自己想的,也不知道是不是最优解,On2时间+On空间
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
int len1=nums1.size(),len2=nums2.size();
vector<int> dic(len2,-1);
stack<int> st;
//先把nums2的表更新出来
for(int i=0;i<len2;i++){
while(!st.empty() && nums2[st.top()]<nums2[i]){
dic[st.top()]=i;
st.pop();
}
st.push(i);
}
for(int i=0;i<len2;i++) cout<<dic[i]<<endl;
vector<int> ans(len1,-1);
for(int i1=0;i1<len1;i1++){
int i2=0;
while(nums1[i1]!=nums2[i2]) i2++;
if(dic[i2]!=-1) ans[i1]=nums2[dic[i2]];
}
return ans;
}
};
Leetcode503. 下一个更大元素 II
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
vector<int> tmp(nums.begin(),nums.end());
nums.insert(nums.end(),tmp.begin(),tmp.end());
int len=nums.size();
if(len==1) return {
-1};
stack<int> st;
vector<int> result(len,-1);
for(int i=0;i<len;i++){
while(!st.empty() && nums[st.top()]<nums[i]){
result[st.top()]=nums[i];
st.pop();
}
st.push(i);
}
result.resize(len/2);
return result;
}
};
Leetcode42. 接雨水
题目链接:Leetcode42. 接雨水
class Solution {
public:
int trap(vector<int>& height) {
//精髓在于:计算每一列能积水的情况
if (height.size() <= 2) return 0;
vector<int> maxLeft(height.size(), 0);
vector<int> maxRight(height.size(), 0);
int size = maxRight.size();
// 记录每个柱子左边柱子最大高度
maxLeft[0] = height[0];
for (int i = 1; i < size; i++) {
maxLeft[i] = max(height[i], maxLeft[i - 1]);
}
// 记录每个柱子右边柱子最大高度
maxRight[size - 1] = height[size - 1];
for (int i = size - 2; i >= 0; i--) {
maxRight[i] = max(height[i], maxRight[i + 1]);
}
// 求和
int sum = 0;
for (int i = 0; i < size; i++) {
int count = min(maxLeft[i], maxRight[i]) - height[i];
if (count > 0) sum += count;
}
return sum;
}
};
Leetcode84.柱状图中最大的矩形
题目链接:Leetcode84.柱状图中最大的矩形
放上我的屎山超时代码,它代表了我一开始的解决思路:计算以当前柱体为高度的最大面积,左右拓宽。
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
int result=0;
int len=heights.size();
if(len==1) return heights[0];
//计算头,只需要看右边
int right=0;
for(int i=1;i<len;i++){
if(heights[i]>=heights[0]) right=i;
else{
result=max(result,heights[0]*(right+1));
break;
}
}
if(right==len-1) result=max(result,heights[0]*(right+1));
//计算尾,只需要看左边
int left=len-1;
for(int i=len-2;i>=0;i--){
if(heights[i]>=heights[len-1]) left=i;
else{
result=max(result,heights[len-1]*(len-left));
break;
}
}
if(left==0) result=max(result,heights[len-1]*(len-left));
//计算中间部分
for(int i=1;i<len-1;i++){
left=i;
right=i;
while(left>0 && heights[left-1]>=heights[i]) left--;
while(right<len-1 && heights[right+1]>=heights[i]) right++;
result=max(result,heights[i]*(right-left+1));
}
return result;
}
};
这是答案给出的:
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
vector<int> minLeftIndex(heights.size());
vector<int> minRightIndex(heights.size());
int size = heights.size();
// 记录每个柱子 左边第一个小于该柱子的下标
minLeftIndex[0] = -1; // 注意这里初始化,防止下面while死循环
for (int i = 1; i < size; i++) {
int t = i - 1;
// 这里不是用if,而是不断向左寻找的过程
while (t >= 0 && heights[t] >= heights[i]) t = minLeftIndex[t];
minLeftIndex[i] = t;
}
// 记录每个柱子 右边第一个小于该柱子的下标
minRightIndex[size - 1] = size; // 注意这里初始化,防止下面while死循环
for (int i = size - 2; i >= 0; i--) {
int t = i + 1;
// 这里不是用if,而是不断向右寻找的过程
while (t < size && heights[t] >= heights[i]) t = minRightIndex[t];
minRightIndex[i] = t;
}
// 求和
int result = 0;
for (int i = 0; i < size; i++) {
int sum = heights[i] * (minRightIndex[i] - minLeftIndex[i] - 1);
result = max(sum, result);
}
return result;
}
};
学到的教训是,时间爆了就想办法用空间换。