目录
一,单调栈
二,具体实现
三,应用
一,单调栈
单调栈是一种基于栈进行的算法。
场景:
给定一个n个数的数组和一个空栈,依次读取n个数,每个数分别入栈一次,出栈一次,最后仍然是空栈。
在这个过程中,需要保持栈里面的元素从栈顶到栈底一直是单调递增(或递减)的,相应的,按照出栈顺序排列的n个数也是单调递增(或递减)的。
n个数的入栈顺序是原数组顺序,但是出栈顺序就不一定了,这个算法就是用来控制,何时入栈,何时出栈。
二,具体实现
现在有一组数10,3,7,4,12。从左到右依次入栈,则如果栈为空或入栈元素值小于栈顶元素值,则入栈;否则,如果入栈则会破坏栈的单调性,则需要把比入栈元素小的元素全部出栈。单调递减的栈反之。
- 10入栈时,栈为空,直接入栈,栈内元素为10。
- 3入栈时,栈顶元素10比3大,则入栈,栈内元素为10,3。
- 7入栈时,栈顶元素3比7小,则栈顶元素出栈,此时栈顶元素为10,比7大,则7入栈,栈内元素为10,7。
- 4入栈时,栈顶元素7比4大,则入栈,栈内元素为10,7,4。
- 12入栈时,栈顶元素4比12小,4出栈,此时栈顶元素为7,仍比12小,栈顶元素7继续出栈,此时栈顶元素为10,仍比12小,10出栈,此时栈为空,12入栈,栈内元素为12。
三,应用
1,数组大小关系计算
ACM模板 这里面的firstInLeft函数就是用单调栈实现的,并且我做了封装,所以很多OJ题目原本要手写单调栈,有了我的模板就可以直接用了。
2,力扣OJ 907. 子数组的最小值之和
给定一个整数数组 A,找到 min(B) 的总和,其中 B 的范围为 A 的每个(连续)子数组。
由于答案可能很大,因此返回答案模 10^9 + 7。
示例:
输入:[3,1,2,4]
输出:17
解释:
子数组为 [3],[1],[2],[4],[3,1],[1,2],[2,4],[3,1,2],[1,2,4],[3,1,2,4]。
最小值为 3,1,2,4,1,1,2,1,1,1,和为 17。
提示:
1 <= A <= 30000
1 <= A[i] <= 30000
//翻转vector
template<typename T>
vector<T> frev(vector<T> v)
{
vector<T> ans;
for(int i=v.size()-1;i>=0;i--)ans.push_back(v[i]);
return ans;
}
//vector乘一个数
template<typename T>
void fcheng(vector<T> &v,int n)
{
for(int i=v.size()-1;i>=0;i--)v[i]*=n;
}
//vector加一个数
template<typename T>
void fjia(vector<T> &v,int n)
{
for(int i=v.size()-1;i>=0;i--)v[i]+=n;
}
//返回vector每个数前面最近的比它小的数的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef(vector<T> v)
{
vector<int> ans;
if(v.size()==0)return ans;
stack<T>st;
st.push(0);
ans.push_back(-1);
for(int i=1;i<v.size();i++)
{
while(!st.empty() && v[st.top()]>=v[i])st.pop();
if(st.empty())ans.push_back(-1);
else ans.push_back(st.top());
st.push(i);
}
return ans;
}
//返回vector每个数后面最近的比它小的数的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
//返回vector每个数前面最近的比它小或等于的数的ID,-1 或者 0到size-1
template<typename T>
vector<int> fminlef2(vector<T> v)
{
vector<int> ans;
if(v.size()==0)return ans;
stack<T>st;
st.push(0);
ans.push_back(-1);
for(int i=1;i<v.size();i++)
{
while(!st.empty() && v[st.top()]>v[i])st.pop();
if(st.empty())ans.push_back(-1);
else ans.push_back(st.top());
st.push(i);
}
return ans;
}
//返回vector每个数后面最近的比它小或等于的数的ID,size 或者 0到size-1
template<typename T>
vector<int> fminrig2(vector<T> v)
{
vector<int>v1=frev(v),v2=fminlef2(v1);
fcheng(v2,-1);
fjia(v2,v.size()-1);
return frev(v2);
}
class Solution {
public:
int sumSubarrayMins(vector<int>& A) {
vector<int>v1=fminlef(A),v2=fminrig2(A);
int ans=0;
for(int i=0;i<A.size();i++)
{
int m=min(i-v1[i],v2[i]-i);
ans+=(v2[i]-v1[i]-m)*m*A[i],ans%=1000000007;
}
return ans;
}
};