「这是我参与11月更文挑战的第4天,活动详情查看:2021最后一次更文挑战」。
前两篇介绍了滑动窗口与单调栈数据结构,接下来上题练习实操下。
一、滑动窗口题目
给定一个正整数数组arr,和一个整数num
某个arr中的子数组sub,如果想达标,必须满足:
sub中最大值 – sub中最小值 <= num,
返回arr中达标子数组的数量
解题前需要得出的2个结论:
public static int getNum(int[] arr, int num) {
if (arr == null || arr.length == 0) {
return 0;
}
LinkedList<Integer> qMin = new LinkedList<>();
LinkedList<Integer> qMax = new LinkedList<>();
int L = 0;
int R = 0;
int res = 0;
while (L < arr.length) {
while (R < arr.length) {
// 小->大
while (!qMin.isEmpty() && arr[qMin.peekLast()] >= arr[R]) {
qMin.pollLast();
}
qMin.addLast(R);
// 大->小
while (!qMax.isEmpty() && arr[qMax.peekLast()] <= arr[R]) {
qMax.pollLast();
}
qMax.addLast(R);
if (arr[qMax.peekFirst()] - arr[qMin.peekFirst()] > num) {
break;
}
R++;
}
res += R - L;
if (qMin.peekFirst() == L) {
qMin.pollFirst();
}
if (qMax.peekFirst() == L) {
qMax.pollFirst();
}
L++;
}
return res;
}
二、单调栈题目
题目1
给定一个只包含正数的数组arr,arr中任何一个子数组sub,
一定都可以算出(sub累加和 )* (sub中的最小值)是什么,
那么所有子数组中,这个值最大是多少?
暴力求解
public static int max1(int[] arr) {
int max = Integer.MIN_VALUE;
int N = arr.length;
for (int i = 0; i < N; i++) {
for (int j = i; j < N; 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, sum * minNum);
}
}
return max;
}
利用单调栈求解
public static int max2(int[] arr) {
int size = arr.length;
int[] sums = new int[size];
sums[0] = arr[0];
for (int i = 1; i < size; i++) {
sums[i] = sums[i - 1] + arr[i];
}
int max = Integer.MIN_VALUE;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < size; i++) {
while (!stack.isEmpty() && arr[stack.peek()] >= arr[i]) {
int j = stack.pop();
max = Math.max(max, (stack.isEmpty() ? sums[i - 1] : (sums[i - 1] - sums[stack.peek()])) * arr[j]);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int j = stack.pop();
max = Math.max(max, (stack.isEmpty() ? sums[size - 1] : (sums[size - 1] - sums[stack.peek()])) * arr[j]);
}
return max;
}
题目2
给定一个非负数组arr,代表直方图,返回直方图的最大长方形面积。
public static int largestRectangleArea(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<>();
int N = height.length;
for (int i = 0; i < N; i++) {
while (!stack.isEmpty() && height[stack.peek()] >= height[i]) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
stack.push(i);
}
while (!stack.isEmpty()) {
int j = stack.pop();
int k = stack.isEmpty() ? -1 : stack.peek();
int curArea = (N - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
三、总结
- 想用滑动窗口,要想办法把具体的问题转化为滑动窗口的处理流程
- 想用滑动窗口最值的更新结构,就看看处理流程下,是否需要最值这个信息
- 想用单调栈,要想办法把具体的问题转化为单调栈所解决的原问题
- 滑动窗口及其最大值和最小值的更新结构、单调栈,都是重要算法原型