单调栈
一种特别设计的栈结构,为了解决如下的问题:
给定一个可能含有重复值的数组arr,i位置的数一定存在如下两个信息
1)arr[i]的左侧离i最近并且小于(或者大于)arr[i]的数在哪?
2)arr[i]的右侧离i最近并且小于(或者大于)arr[i]的数在哪?
// arr = [ 3, 1, 2, 3]
// 0 1 2 3
// [
// 0 : [-1, 1]
// 1 : [-1, -1]
// 2 : [ 1, -1]
// 3 : [ 2, -1]
// ]
// 返回二维数组代表 一条条信息 无重复信息的版本
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++) { // 当遍历到i位置的数,arr[i]
while (!stack.isEmpty() && arr[stack.peek()] > arr[i]) {
int j = stack.pop(); // 当前数落不下去的时候
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
res[j][0] = leftLessIndex;
res[j][1] = i;
}
stack.push(i);
}
// 栈里还有东西,单独结算的,其实直接复制下来就好了
while (!stack.isEmpty()) {
int j = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek();
res[j][0] = leftLessIndex;
res[j][1] = -1;
}
return res;
}
// 有重复的情况
public static 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++) { // i -> arr[i] 进栈
// 只有严格的才去结算
while (!stack.isEmpty() && arr[stack.peek().get(0)] > arr[i]) {
List<Integer> popIs = stack.pop();
// 拿到底下压着链表的最后一个位置
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
for (Integer popi : popIs) {
res[popi][0] = leftLessIndex;
res[popi][1] = i;
}
}
if (!stack.isEmpty() && arr[stack.peek().get(0)] == arr[i]) {
// 如果是相等的,直接加到堆顶中的链表当中
stack.peek().add(Integer.valueOf(i));
} else {
// 注意这里用ArrayList,因为用LinkedList如果重复多的话会慢
ArrayList<Integer> list = new ArrayList<>();
// 如果是小于的,自己建好一个小链表,把小链表添加进去
list.add(i);
stack.push(list);
}
}
while (!stack.isEmpty()) {
List<Integer> popIs = stack.pop();
int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
for (Integer popi : popIs) {
res[popi][0] = leftLessIndex;
res[popi][1] = -1;
}
}
return res;
}