题目
一个含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比arr[i]小的位置,返回所有相应的信息。
举例:arr={3,4,1,5,6,2,7},返回如下的二维数组作为结果:{{-1,2},{0,2},{-1,-1},{2,5},{3,5},{2,-1},{5,-1}},其中-1表示不存在
要求
如果arr长度为N,时间复杂度达到O(N)。
思路
数据结构:单调栈,依旧存放数组元素的索引,但是因为会存在重复的数字,如果依旧和初阶问题一样直接存索引的话,会存在问题,所以栈里存放列表,列表里存放索引,相同的索引存在同一个列表中
使用一个栈,并且使该栈从栈顶到栈底的值是严格递减的(因为找的是左右两边比当前位置小的值)。
流程:
- 遍历数组
- 栈为空或者当前arr[i]大于栈顶中列表的第一个元素(因为同一个列表中存放的索引值不同但是对应的数组值都是一样的)对应的数组值,直接新建一个列表,将当前i加入列表并入栈
- 若栈不为空,并且当前arr[i]小于栈顶列表中第一个元素 j(假设为j,因为栈里存放的是数组索引值,这里比较的是arr[i]<arr[j]),则需要弹出栈顶元素(即弹出一个列表),则对于弹出列表中最后插入的元素j来说左边位置离j最近并且小于arr[j]的值为当前栈顶对应的值,右边位置则是i;
- 若栈不为空,并且当前arr[i]小于栈顶列表中第一个元素 j(假设为j,因为栈里存放的是数组索引值,这里比较的是arr[i]=arr[j]),如果两个值相等则将当前i,直接加入当前栈顶列表。
- 遍历完数组后,若栈不为空,则对于栈中的所有元素,它们的右边位置最近并且小于他们的值都不存在即为-1;依次弹出栈顶列表,并对列表中的元素 (假设为j)迭代循环,j的右边为-1,左边位置为当前栈顶列表中最后插入的值;完成
源码
public int[][] getNearLess(int[] arr){
//初始化结果数组
int[][] res=new int[arr.length][2];
//初始化单调栈
Stack<List<Integer>> stack=new Stack<>();
//遍历数组
for(int i=0;i<arr.length;i++){
//若栈不为空,并且当前数组值小于栈顶列表中第一个元素所对应的数组值
while(!stack.isEmpty()&&arr[stack.peek().get(0)]>arr[i]){
//弹出栈顶的列表
List<Integer> popIndex=stack.pop();
//取列表中加入最晚的
int leftLessIndex=stack.isEmpty()?-1:stack.peek().get(stack.peek().size()-1);
int rightLessIndex=i;
for(Integer popi:popIndex){
res[popi][0]=leftLessIndex;
res[popi][1]=rightLessIndex;
}
}
//若栈不为空,并且当前数组值等于栈顶列表中第一个元素所对应的数组值
if(!stack.isEmpty()&&arr[stack.peek().get(0)]==arr[i]){
stack.peek().add(Integer.valueOf(i));
}else{
//若栈为空或者并且当前数组值大于栈顶列表中第一个元素所对应的数组值
ArrayList<Integer> list=new ArrayList<Integer>();
list.add(i);
stack.push(list);
}
}
//同初阶问题,数组遍历完后,对栈进行清洗
while(!stack.isEmpty()){
List<Integer> popIndex=stack.pop();
int leftLessIndex=stack.isEmpty()?-1:stack.peek().get(stack.peek().size()-1);
int rightLessIndex=-1;
for(Integer popi:popIndex){
res[popi][0]=leftLessIndex;
res[popi][1]=rightLessIndex;
}
}
return res;
}
欢迎随时交流讨论