1.滑动窗口
双端队列:队列的头和尾都可以压入和弹出
public static int[] getMaxWindow(int[] arr, int w) {
if (arr == null || w < 1 || arr.length < w) {
return null;
}
// qmax 窗口最大值的更新结构
// qmax 中放数组的下标
//linkedList可以当作双端队列
LinkedList<Integer> qmax = new LinkedList<Integer>();
//放找出的最大结果
int[] res = new int[arr.length - w + 1];
int index = 0;
for (int R = 0; R < arr.length; R++) {
//把双端队列中小于等于当前值从尾部弹出,并从尾部放入当前值
while (!qmax.isEmpty() && arr[qmax.peekLast()] <= arr[R]) {
qmax.pollLast();
}
qmax.addLast(R);
//从头部弹出双端队列中不在当前窗口中的值
if (qmax.peekFirst() == R - w) {
qmax.pollFirst();
}
//判断窗口的长度是否到达w
if (R >= w - 1) {
res[index++] = arr[qmax.peekFirst()];
}
}
return res;
}
2.单调栈
数组中无重复值
找出,数组中的一个值,左侧最靠近的比其小的值,其右侧离其最近的小于自己的值
栈中遵循小在下,大在上,入栈时,如果要入栈的值(2->2)比栈顶的值(1->4)小,再入栈就破坏了栈底小栈顶大的规则,要将栈顶(1->4)弹出,弹出的值(1->4)的右侧离其最近的小于自己的值就是,即将入栈的值(2->2),左侧最靠近的比其小的值就是其栈中压在下面的第一个值(0-3)
// 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) {
//0位置放左测最近小与的值,1位置放右测最近小与的值
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<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;
}
利用单调栈
以数组中的一个位置为点,计算出,一位置上值为最小值的,最长的连续子数组,即找出左侧最靠近的比其小的值,其右侧离其最近的小于自己的值,中间的连续数组
暴力遍历
public static int max1(int[] arr) {
int max = Integer.MIN_VALUE;
for (int i = 0; i < arr.length; i++) {
for (int j = i; j < arr.length; j++) {
int minNum = Integer.MAX_VALUE;
int sum = 0;
for (int k = i; k <= j; k++) {
sum += arr[k];
minNum = Math.min(minNum, arr[k]);
}
max = Math.max(max, minNum * sum);
}
}
return max;
}
单调栈