栈与队列7——单调栈结构(进阶问题)

题目

一个含有重复值的数组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)。


思路

数据结构:单调栈,依旧存放数组元素的索引,但是因为会存在重复的数字,如果依旧和初阶问题一样直接存索引的话,会存在问题,所以栈里存放列表,列表里存放索引,相同的索引存在同一个列表中

使用一个栈,并且使该栈从栈顶到栈底的值是严格递减的(因为找的是左右两边比当前位置小的值)。

流程:

  1.     遍历数组
  2.     栈为空或者当前arr[i]大于栈顶中列表的第一个元素(因为同一个列表中存放的索引值不同但是对应的数组值都是一样的)对应的数组值,直接新建一个列表,将当前i加入列表并入栈
  3.     若栈不为空,并且当前arr[i]小于栈顶列表中第一个元素 j(假设为j,因为栈里存放的是数组索引值,这里比较的是arr[i]<arr[j]),则需要弹出栈顶元素(即弹出一个列表),则对于弹出列表中最后插入的元素j来说左边位置离j最近并且小于arr[j]的值为当前栈顶对应的值,右边位置则是i;
  4. 若栈不为空,并且当前arr[i]小于栈顶列表中第一个元素 j(假设为j,因为栈里存放的是数组索引值,这里比较的是arr[i]=arr[j]),如果两个值相等则将当前i,直接加入当前栈顶列表。
  5.     遍历完数组后,若栈不为空,则对于栈中的所有元素,它们的右边位置最近并且小于他们的值都不存在即为-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;
}

欢迎随时交流讨论

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值