「这是我参与2022首次更文挑战的第41天,活动详情查看:2022首次更文挑战」。
一、题目一
给定一个只包含正数的数组arr,arr中任何一个子数组sub,
一定都可以算出(sub累加和 ) * (sub中的最小值)是什么,
那么所有子数组中,这个值最大是多少?
1、分析
首先子数组一定是连续的
如果子数组以i位置的x作为最小值,那么如何以x作为最小值的子数组累加和最大呢?x向左扩,扩到左边离x最近且小于x时停止,x向右扩,扩到右边离x最近且小于x时停止,这是不是单调栈的特性?yes
所以每个位置都这么干,max下,答案必在其中
sub累加和不用遍历的方式得到,用前缀和
在单调栈的结构上做了下改动(弹栈规则),等于的数也弹栈,没有用链表存相同的数
2、实现
2.1、暴力实现
// 暴力实现,时间复杂度O(N³)
public static int maxSumMinProduct(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;
}
2.2、单调栈(系统栈)
public static int maxSumMinProduct(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.3、单调栈(数组实现栈)
// 替代系统实现的栈,也会快很多,题目要求的取模
public static int maxSumMinProduct(int[] arr) {
int size = arr.length;
long[] sums = new long[size];
sums[0] = arr[0];
for (int i = 1; i < size; i++) {
sums[i] = sums[i - 1] + arr[i];
}
long max = Long.MIN_VALUE;
int[] stack = new int[size];
int stackSize = 0; // 判断栈是否满的标记
for (int i = 0; i < size; i++) {
while (stackSize != 0 && arr[stack[stackSize - 1]] >= arr[i]) {
int j = stack[--stackSize];
max = Math.max(max,
(stackSize == 0 ? sums[i - 1] : (sums[i - 1] - sums[stack[stackSize - 1]])) * arr[j]);
}
stack[stackSize++] = i;
}
while (stackSize != 0) {
int j = stack[--stackSize];
max = Math.max(max,
(stackSize == 0 ? sums[size - 1] : (sums[size - 1] - sums[stack[stackSize - 1]])) * arr[j]);
}
return (int) (max % 1000000007);
}
二、题目二
给定一个非负数组arr,代表直方图
返回直方图的最大长方形面积
1、分析
连通性问题,当出现等于的数时,不结算,等后边的数冲掉,不影响结果
长度计算问题
2、实现
2.1、单调栈(系统栈)
public static int largestRectangleArea(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int maxArea = 0;
Stack<Integer> stack = new Stack<>();
for (int i = 0; i < height.length; i++) {
while (!stack.isEmpty() && height[i] <= height[stack.peek()]) {
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 = (height.length - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
2.2、单调栈(数组实现栈)
public static int largestRectangleArea(int[] height) {
if (height == null || height.length == 0) {
return 0;
}
int N = height.length;
int[] stack = new int[N];
int si = -1;
int maxArea = 0;
for (int i = 0; i < height.length; i++) {
while (si != -1 && height[i] <= height[stack[si]]) {
int j = stack[si--];
int k = si == -1 ? -1 : stack[si];
int curArea = (i - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
stack[++si] = i;
}
while (si != -1) {
int j = stack[si--];
int k = si == -1 ? -1 : stack[si];
int curArea = (height.length - k - 1) * height[j];
maxArea = Math.max(maxArea, curArea);
}
return maxArea;
}
三、总结
单调栈在面试中的比例挺大的,能解决很多问题,必须把单调栈的执行流程弄明白。