最近在学栈,看到一个挺经典的题,思路有些曲折,特自写图解。
题目
题目一
给定一个不含重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比 arr[i] 小的位置,返回所有位置相应的信息。(-1表示不存在)
题目二
给定一个可能含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比 arr[i] 小的位置,返回所有位置相应的信息。(-1表示不存在)
题目要求
时间复杂度为O(N)
核心思想
二者共同的核心
设计一个存放元素为数组位置的栈,保证stack从栈底到栈顶严格递增。
如果能保证,则放入元素;如果不能保证,则弹出栈顶元素。
对弹出的元素来说,当前所遍历到的位置就是右边最近的更小元素,栈中位于弹出元素下面相邻位置的就是左边最近的更小元素。左右无满足此条件的元素时,则满足题意的元素不存在。
若遍历结束后,栈中还存在元素,则对栈中元素进行弹出清算。
有重复值时需要做出的变化
值相同时仍入栈;当出栈时,相邻的相同元素同时出栈。
结果应保留最晚加入的那个。
举个栗子
arr = { 3,4,1,5,6,2,7};
图中,
- []表示位置,也就是数组下标
- ()表示元素值,也就是存放的数值是多少
黑色表示挨个放入的流程,蓝色表示放入当前元素后带来的变化,并且在有结果时,把结果标注在了栈下面。
图解流程(无重复)
图解流程(有重复)
例如,arr[] = {3,1,3,4,3,5,3,2,2}
代码怎么写
无重复值
#include<iostream>
#include<algorithm>
#include<stack>
using namespace std;
int* function(int* arr,int l){
static int* ans = new int[2*l];
stack<int> s;
for(int i = 0;i < l;i++){
while(!s.empty() && arr[s.top()] > arr[i]){
int popIndex = s.top();
s.pop();
int leftLessIndex = s.empty()? -1 : s.top();
ans[2*popIndex] = leftLessIndex;
ans[2*popIndex+1] = i;
}
s.push(i);
}
while(!s.empty()){
int popIndex = s.top();
s.pop();
int leftLessIndex = s.empty()? -1 : s.top();
ans[2*popIndex] = leftLessIndex;
ans[2*popIndex+1] = -1;
}
return ans;
}
//测试的数据就是例子里的数据
int main(){
int arr[] = {3,4,1,5,6,2,7};
int* ans = function(arr,7);
for(int i = 0;i < 14;i+=2){
cout<<ans[i]<<"\t"<<ans[i+1]<<"\n";
}
return 0;
}
控制台输出与上文推导结果相同
注:由于C++写二维数组比较麻烦,所以采用一维数组的存储,最后按二维数组的格式进行输出。
有重复值
可以考虑把栈中每一层存的元素从int变成vector<int>,相同的值存在同一层。
变式题
把“找到每一个i位置左边和右边离i位置最近且值比 arr[i] 小的位置”改成值更大
一些应用
看看大佬的总结吧