今天是算法学习的第三十八天,主要学习了单调栈算法以及相关习题。单调栈算法的作用是找出左边或右边第一个大于或小于当前值的下标。
739. 每日温度
这个题目是给出一个数组,要求找到每一个元素右边第一个比他大的元素。这就是一个很典型的单调栈的问题。解法就是如果遍历的当前元素大于栈顶元素,就持续弹出栈顶元素,并对弹出元素进行赋值。意味着找到了右边第一个比这个元素大的元素。具体代码实现如下所示:
cpp
class Solution {
public:
vector<int> dailyTemperatures(vector<int>& temperatures) {
vector<int> vec(temperatures.size(),0);
stack<int> st;
for(int i=0;i<temperatures.size();i++)
{
while(!st.empty()&&temperatures[i]>temperatures[st.top()])
{
vec[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
while(!st.empty())
{
vec[st.top()]=0;
st.pop();
}
return vec;
}
};
496.下一个更大元素 I
题目链接:496. 下一个更大元素 I - 力扣(LeetCode)
这个题目其实是差不多的,首先计算得到nums2的每个元素右边第一个比他大的元素,然后对nums1的所有元素进行遍历操作,找到相同的值就直接其对应的右边第一个比他大的元素下标即可。具体代码实现如下所示:
cpp
class Solution {
public:
vector<int> nextGreaterElement(vector<int>& nums1, vector<int>& nums2) {
stack<int> st;
vector<int> vec(nums2.size(),0);
vector<int> result;
for(int i=0;i<nums2.size();i++)
{
while(!st.empty()&&nums2[i]>nums2[st.top()])
{
vec[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
while(!st.empty())
{
vec[st.top()]=-1;
st.pop();
}
for(int i=0;i<nums1.size();i++)
{
for(int j=0;j<nums2.size();j++)
{
if(nums1[i]==nums2[j])
{
if(vec[j]==-1) result.push_back(-1);
else result.push_back(nums2[j+vec[j]]);
break;
}
}
}
return result;
}
};
503.下一个更大元素II
题目链接:503. 下一个更大元素 II - 力扣(LeetCode)
这个题是一个数组为循环数组,找到右边第一个比他大的元素。我的做法就直接把这个数组进行一个复制并扩充,然后进行单调栈算法的实施。时间复杂度好像是有点高的,但是能过。具体代码实现如下所示:
cpp
class Solution {
public:
vector<int> nextGreaterElements(vector<int>& nums) {
stack<int> st;
vector<int> vec(2*nums.size(),0);
vector<int> dou_nums(2*nums.size(),0);
for(int i=0;i<nums.size();i++)
{
dou_nums[i]=nums[i];
dou_nums[nums.size()+i]=nums[i];
}
for(int i=0;i<dou_nums.size();i++) cout<<dou_nums[i]<<' ';
cout<<endl;
vector<int> result;
for(int i=0;i<dou_nums.size();i++)
{
while(!st.empty()&&dou_nums[i]>dou_nums[st.top()])
{
vec[st.top()]=i-st.top();
st.pop();
}
st.push(i);
}
while(!st.empty())
{
vec[st.top()]=-1;
st.pop();
}
for(int i=0;i<vec.size()/2;i++)
{
if(vec[i]==-1) result.push_back(-1);
else result.push_back(dou_nums[vec[i]+i]);
}
return result;
}
};
42. 接雨水
这个就是一道比较经典的难题了。思路是对于每一个柱子,找到左边和右边第一个比它大的,然后就可以求解一个雨水块。找到左边和右边第一个比他大的过程就是使用单调栈完成。使用的是单调递增栈。如果遍历元素大于栈顶元素,取栈顶元素作为中间值并弹出,选择遍历元素和此时的栈顶元素作为水块的左右边界,中间元素作为水块的高。直接相乘可以得到水块的面积,这样累加的结果就是总面积。具体代码实现如下所示:
cpp
class Solution {
public:
int trap(vector<int>& height) {
stack<int> st;
int sum=0;
if(height.size()==0) return 0;
st.push(0);
for(int i=1;i<height.size();i++)
{
while(!st.empty()&&height[i]>height[st.top()])
{
int mid=st.top();
st.pop();
if(!st.empty())
{
int h=min(height[i],height[st.top()])-height[mid];
int w=i-st.top()-1;
sum+=h*w;
}
}
st.push(i);
}
return sum;
}
};
84.柱状图中最大的矩形
题目链接:84. 柱状图中最大的矩形 - 力扣(LeetCode)
这个题目跟接雨水还是挺像的。区别在于这个题目对于遍历元素是求解左右两边第一个这个元素小的元素,所以使用的是单调递减栈。除此之外,这个题有一地方要处理一下,就是对数组的首尾都添上0,不然遇到单调递增或单调递减的数组就无法求解。具体代码实现如下所示:
cpp
class Solution {
public:
int largestRectangleArea(vector<int>& heights) {
heights.insert(heights.begin(),0);
int max_area=0;
heights.push_back(0);
stack<int> st;
st.push(0);
for(int i=1;i<heights.size();i++)
{
while(!st.empty()&&heights[i]<heights[st.top()])
{
int mid=st.top();
st.pop();
if(!st.empty())
{
int h=heights[mid];
int w=i-st.top()-1;
max_area=max(max_area,h*w);
}
}
st.push(i);
}
return max_area;
}
};