单调栈面试题

244 阅读2分钟

「这是我参与2022首次更文挑战的第41天,活动详情查看:2022首次更文挑战」。

一、题目一

给定一个只包含正数的数组arr,arr中任何一个子数组sub,

一定都可以算出(sub累加和 ) * (sub中的最小值)是什么,

那么所有子数组中,这个值最大是多少?

LeetCode

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,代表直方图

返回直方图的最大长方形面积

LeetCode

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;
}

三、总结

单调栈在面试中的比例挺大的,能解决很多问题,必须把单调栈的执行流程弄明白。

单调栈学习