题目大意:
给定数组a,分别寻找i的左边和右边比a[i]大的第一个数字的下标。
思路分析:
此题暴力及爽,但是时间复杂度过大(但是我没试能不能过,有兴趣的可以试试),可以推出最坏情况下,因为对于每个数,向左右延伸至头尾元素,即n个元素每个查询n次。
但是对于这种寻找最近的且有大小关系的题目,很容易想到单调栈。此题便可以利用单调栈去寻找L[i]和R[i],思路还是很清晰的,我是用数组模拟了栈。代码如下:
int solution(int n, std::vector<int> array) {
// Edit your code here
const int N=n;
int l[N+10], L[N+10], cntl=0;//l:单调栈 L:题意 cntl:栈内元素个数
int r[N+10], R[N+10], cntr=0;
for (int i=0; i<=n; i++)
l[i]=r[i]=L[i]=R[i]=-1;
cntl++; l[cntl]=0;
for (int i=1; i<n; i++){
while (cntl>0 && array[l[cntl]]<=array[i]){
l[cntl]=-1; cntl--;
}//不断寻找直到小于栈最小元素,或栈为空
if (cntl>0){
L[i]=l[cntl];
cntl++; l[cntl]=i;
}else{
cntl++; l[cntl]=i;
}
}
cntr++; r[cntr]=n-1;
for (int i=n-1; i>=0; i--){
while (cntr>0 && array[r[cntr]]<=array[i]){
r[cntr]=-1;
cntr--;
}//不断寻找直到小于栈最小元素,或栈为空
if (cntr>0){
R[i]=r[cntr];
cntr++;
r[cntr]=i;
}else{
cntr++;
r[cntr]=i;
}
}
for (int i=0; i<n; i++){
L[i]++; R[i]++;
}
int ans=0;
for (int i=0; i<n; i++){
ans=fmax (ans, L[i]*R[i]);
}
return ans;
}
知识回顾:
单调栈即为满足单调性的栈结构。
我们往往会遇到求递减or递增子序列的问题,而那往往使用动态规划,但是和栈有什么关系呢(其实这是笔者一开始理解的误区,可能大家感觉思维有些跳脱,但是一开始确实有过很多疑惑和混淆概念)?虽然往栈里面塞元素,再通过取出塞入能够去维持序列单调性,但是对于所需的最长or最少划分是无用的,那么这样得到一个单调的子序列会有什么特性吗?
我们可以来看单调栈的操作:如果我们想要维持栈内元素单调增or减,直接向栈里面加入元素显然是不可以的。以递减的栈为例,令当前元素为now,栈的最顶端元素为last,如果last>now,我们可以直接将now压入栈,如果last<=now,我们可以把栈的元素一个个取出来,直到当前last>now,这样将now压入栈的时候,可以维持栈内元素的单调性。
于是有细心的人就可以发现,每次寻找的到的那个last,是在now之前第一个比now大的值,可以反证:若last不是第一个,那么在两者之间至少存在一个temp大于now,设temp是第一个大于now的元素,在处理temp时,如果temp>=last,那么temp会将last出栈,now只会查询到temp,而与已经出栈的last无关,如果temp<last,那么temp必然会在last后面进栈,在处理now时,查询到temp即可停止。(至于说temp不是第一个大于now的元素,那temp不就相当于上面last的位置,而第一个是上面的temp)(doge)
所以单调栈很适合查询左or右第一个大于或小于当前数字,时间复杂度为。
以此衍生的一个经典的问题就是柱状图中最大矩形面积,使用单调栈解题的思路就是:以当前高度为矩形的高,求距离当前柱子左右第一个比他小的元素位置,这样就能求出可覆盖的最大宽度,然后n个矩形对比面积。
特别感谢
作为一个曾经的野鸡学校的三流OIer,我的代码一直不怎么规范,谢谢豆包一直提醒我帮我纠正!回头就去用vector和stack写一遍!