单调栈结构
题目
给定一个不含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比arr[i] 小的位置。返回所有位置的相应信息。
题目变形
给定一个不含有重复值的数组arr,找到每一个i位置左边和右边离i位置最近且值比arr[i] 大的位置。返回所有位置的相应信息。
举例说明
arr = {3,4,1,5,6,2,7}
返回结果:
{
{-1,2},// 其中-1表示不存在
{0,2},
{-1,-1},
{2,5}.
{3,5},
{2,-1},
{5,-1}
}
本题可以以时间复杂度为O(N^2)来进行实现,每个位置向左向右进行遍历,可以进行确定,但是如果想让时间复杂度做到O(N),就需要使用到单调结构。
单调栈的思路
准备一个栈,记为Stack,在栈中存放的元素是数组的位置,开始时栈为空。如果找到一个i位置左边和右边离i位置最近且值比a r r[i]小的位置,那么stack从栈底到栈顶的位置所代表的值是严格递增的。如果找到一个i位置左边和右边离i位置最近且值比a r r[i]大的位置,那么stack从栈底到栈顶的位置所代表的值是严格递减的。本题利用的结构是前者。
单调栈的操作核心
本题中单调栈的核心就在于从栈底到栈顶要求的顺序是严格递增的。
- 栈为空时,遍历数组直接放入栈中。
- 当栈不为空时,遍历数组的值要与栈中所存的位置数据对应的数值进行比较,如果符合遍历数组的值大于栈顶的值,则直接放入栈中。
- 如果是小于的话。栈顶的元素要进行弹出的操作,并且要进行清算的。
清算的原则
- 如果X位置 被弹出,在栈中位于X位置下面的位置,就是X位置左边离X位置最近且值比arr[i]小的位置。当前遍历到位置就是X位置右侧离X位置最近且值比arr[i]小的位置。
- 遍历阶段结束,清算栈中剩下的位置:弹出X的位置,则X位置下面的位置就是左侧离X最近且值要比X位置对应的值要小的位置,右侧就是不存在的位置,即-1。
代码
import java.util.Stack;
public class Code_03_stack {
//
public static int [][] getNearLessNorepeat(int[] arr){
int[][] res = new int[arr.length][2];
/**
* 单调栈
*/
Stack<Integer> stack = new Stack<>();
for (int i = 0; i <arr.length ; i++) {
/**
* 若当前位置元素小于栈顶元素,则一直弹出栈顶元素,直到不小于位置,
*/
while (!stack.isEmpty() && arr[i] < stack.peek()){
int popIndex = stack.pop();
/**
* 栈中当前元素上方的元素
*/
int leftLessIndex = stack.isEmpty()? -1:stack.peek();
/**
* 记录弹出元素【左侧】,【第一个小于弹出元素的元素的下标】
*/
res[popIndex][0] = leftLessIndex;
res[popIndex][1] = i;
}
/**
* 当前元素进行入栈操作
*/
stack.push(i);
}
/**
* 遍历完成对其他的元素进行清算的功能
*/
while (!stack.isEmpty()){
int popIndex = stack.pop();
int leftIndex = stack.isEmpty()?-1:stack.peek();
res[popIndex][0] = leftIndex;
res[popIndex][1] = -1;
}
return res;
}
}
进阶题目
可能含有重复数的数组该如何使用单调栈呢?