第25节.pptx

单调栈结构

一种特别设计的栈结构,为了解决如下的问题:
给定一个可能含有重复值的数组arr,i位置的数一定存在如下两个信息
1)arr[i]的左侧离i最近并且小于(或者大于)arr[i]的数在哪?
2)arr[i]的右侧离i最近并且小于(或者大于)arr[i]的数在哪?
如果想得到arr中所有位置的两个信息,怎么能让得到信息的过程尽量快。

单调栈实现

  1. public static int[][] getNearLessNoRepeat(int[] arr) {
  2. int[][] ans = new int[arr.length][2];
  3. Stack<Integer> stack = new Stack<>();
  4. for (int i = 0; i < arr.length; i++) {
  5. while (!stack.empty() && arr[i] < arr[stack.peek()]) {
  6. int index = stack.pop();
  7. ans[index][0] = stack.empty() ? -1 : stack.peek();
  8. ans[index][1] = i;
  9. }
  10. stack.push(i);
  11. }
  12. while (!stack.empty()) {
  13. int index = stack.pop();
  14. ans[index][0] = stack.empty() ? -1 : stack.peek();
  15. ans[index][1] = -1;
  16. }
  17. return ans;
  18. }
  1. public static int[][] getNearLess(int[] arr) {
  2. int[][] ans = new int[arr.length][2];
  3. Stack<LinkedList<Integer>> stack = new Stack<>();
  4. for (int i = 0; i < arr.length; i++) {
  5. while (!stack.empty() && arr[i] < arr[stack.peek().peekLast()]) {
  6. LinkedList<Integer> list = stack.pop();
  7. int leftIndex = stack.empty() ? -1 : stack.peek().peekLast();
  8. for (int index : list) {
  9. ans[index][0] = leftIndex;
  10. ans[index][1] = i;
  11. }
  12. }
  13. if (!stack.empty() && arr[i] == arr[stack.peek().peekLast()]) {
  14. stack.peek().offerLast(i);
  15. } else {
  16. LinkedList<Integer> list = new LinkedList<>();
  17. list.offerLast(i);
  18. stack.push(list);
  19. }
  20. }
  21. while (!stack.empty()) {
  22. LinkedList<Integer> list = stack.pop();
  23. int leftIndex = stack.empty() ? -1 : stack.peek().peekLast();
  24. for (int index : list) {
  25. ans[index][0] = leftIndex;
  26. ans[index][1] = -1;
  27. }
  28. }
  29. return ans;
  30. }

单调栈题目

题目1:满足要求的子数组的最大值

给定一个只包含正数的数组arr,arr中任何一个子数组sub,一定都可以算出(sub累加和 )* (sub中的最小值)是什么,那么所有子数组中,这个值最大是多少?

  1. public static int maxValue2(int[] arr) {
  2. if (arr == null || arr.length == 0) {
  3. return Integer.MIN_VALUE;
  4. }
  5. int ans = Integer.MIN_VALUE;
  6. for (int i = 0; i < arr.length; i++) {
  7. for (int j = i; j < arr.length; j++) {
  8. int min = Integer.MAX_VALUE;
  9. int sum = 0;
  10. for (int k = i; k <= j; k++) {
  11. min = Math.min(min, arr[k]);
  12. sum += arr[k];
  13. }
  14. ans = Math.max(ans, sum * min);
  15. }
  16. }
  17. return ans;
  18. }
  1. public static int maxValue(int[] arr) {
  2. if (arr == null || arr.length == 0) {
  3. return Integer.MIN_VALUE;
  4. }
  5. int n = arr.length;
  6. int[] preSum = new int[n];
  7. preSum[0] = arr[0];
  8. for (int i = 1; i < n; i++) {
  9. preSum[i] = preSum[i - 1] + arr[i];
  10. }
  11. return process(arr, preSum);
  12. }
  13. private static int process(int[] arr, int[] preSum) {
  14. int ans = 0;
  15. int[] stack = new int[arr.length];
  16. int cursor = -1;
  17. for (int i = 0; i < arr.length; i++) {
  18. while (cursor != -1 && arr[stack[cursor]] >= arr[i]) {
  19. int index = stack[cursor--];
  20. int left = cursor == -1 ? -1 : stack[cursor];
  21. int right = i;
  22. ans = left == -1 ? Math.max(ans, arr[index] * preSum[right - 1]) : Math.max(ans, arr[index] * (preSum[right - 1] - preSum[left]));
  23. }
  24. stack[++cursor] = i;
  25. }
  26. while (cursor != -1) {
  27. int index = stack[cursor--];
  28. int left = cursor == -1 ? -1 : stack[cursor];
  29. int right = arr.length;
  30. ans = left == -1 ? Math.max(ans, arr[index] * preSum[right - 1]) : Math.max(ans, arr[index] * (preSum[right - 1] - preSum[left]));
  31. }
  32. return ans;
  33. }

题目2:返回直方图的最大长方形面积

给定一个非负数组arr,代表直方图,返回直方图的最大长方形面积
https://leetcode.com/problems/largest-rectangle-in-histogram


public static int largestRectangleArea(int[] heights) {
    if (heights == null || heights.length == 0) {
        return 0;
    }
    int n = heights.length;
    int[] stack = new int[n];
    int cursor = -1;
    int area = 0;
    for (int i = 0; i < n; i++) {
        while (cursor != -1 && heights[stack[cursor]] >= heights[i]) {
            int index = stack[cursor--];
            int left = cursor == -1 ? -1 : stack[cursor];
            int right = i;
            area = left == -1 ? Math.max(area, heights[index] * right) : Math.max(area, heights[index] * (right - 1 - left));
        }
        stack[++cursor] = i;
    }
    while (cursor != -1) {
        int index = stack[cursor--];
        int left = cursor == -1 ? -1 : stack[cursor];
        int right = n;
        area = left == -1 ? Math.max(area, heights[index] * right) : Math.max(area, heights[index] * (right - 1 - left));
    }
    return area;
}

题目3:全部由1组成的最大子矩形,内部有多少个1

给定一个二维数组matrix,其中的值不是0就是1,返回全部由1组成的最大子矩形,内部有多少个1
https://leetcode.com/problems/maximal-rectangle/submissions/

public static int maximalRectangle(char[][] matrix) {
    if (matrix == null || matrix.length == 0 || matrix[0] == null || matrix[0].length == 0) {
        return 0;
    }
    int n = matrix.length;
    int m = matrix[0].length;
    int[] height = new int[m];
    int area = 0;
    for (char[] row : matrix) {
        for (int i = 0; i < m; i++) {
            height[i] = row[i] == '0' ? 0 : height[i] + 1;
        }
        area = Math.max(area, largestRectangleArea(height));
    }
    return area;
}

private static int largestRectangleArea(int[] heights) {
    int n = heights.length;
    int[] stack = new int[n];
    int cursor = -1;
    int area = 0;
    for (int i = 0; i < n; i++) {
        while (cursor != -1 && heights[stack[cursor]] >= heights[i]) {
            int index = stack[cursor--];
            int left = cursor == -1 ? -1 : stack[cursor];
            int right = i;
            area = Math.max(area, heights[index] * (right - 1 - left));
        }
        stack[++cursor] = i;
    }
    while (cursor != -1) {
        int index = stack[cursor--];
        int left = cursor == -1 ? -1 : stack[cursor];
        int right = n;
        area = Math.max(area, heights[index] * (right - 1 - left));
    }
    return area;
}

题目4:矩形数量

给定一个二维数组matrix,其中的值不是0就是1,返回全部由1组成的子矩形数量
https://leetcode.com/problems/count-submatrices-with-all-ones/submissions/

public static int numSubmat(int[][] mat) {
    if (mat == null || mat.length == 0 || mat[0] == null || mat.length == 0) {
        return 0;
    }

    int m = mat[0].length;
    int[] heights = new int[m];
    int num = 0;
    for (int[] rows : mat) {
        for (int i = 0; i < m; i++) {
            heights[i] = rows[i] == 0 ? 0 : heights[i] + 1;
        }
        num += rectNum(heights);
    }
    return num;
}

private static int rectNum(int[] height) {
    if (height == null || height.length == 0) {
        return 0;
    }
    int n = height.length;
    int[] stack = new int[n];
    int num = 0;
    int cursor = -1;
    for (int i = 0; i < n; i++) {
        while (cursor != -1 && height[stack[cursor]] >= height[i]) {
            int index = stack[cursor--];

            int left = cursor == -1 ? -1 : stack[cursor];
            int right = i;
            int maxMinIndex = left == -1 ? right : height[left] > height[right] ? left : right;

            num += (height[index] - height[maxMinIndex]) * num(right - left - 1);

        }
        stack[++cursor] = i;
    }
    while (cursor != -1) {
        int index = stack[cursor--];
        int left = cursor == -1 ? -1 : stack[cursor];
        int right = n;
        int maxMinIndex = left == -1 ? -1 : left;
        int down = maxMinIndex == -1 ? 0 : height[left];
        num += (height[index] - down) * num(right - left - 1);
    }
    return num;
}

private static int num(int n) {
    return (n * (n + 1)) >> 1;
}

题目5:所有子数组最小值的累加和

给定一个数组arr,返回所有子数组最小值的累加和
https://leetcode.com/problems/sum-of-subarray-minimums/submissions/

public static int sumSubarrayMins(int[] arr) {
    int n = arr.length;
    int[] stack = new int[n];
    int cursor = -1;
    long sum = 0L;

    for (int i = 0; i < n; i++) {
        while (cursor != -1 && arr[stack[cursor]] >= arr[i]) {
            int index = stack[cursor--];
            int left = cursor == -1 ? -1 : stack[cursor];
            int right = i;

            int num = (index - left) * (right - index);
            sum += (num * (long) arr[index]);
            sum %= 1000000007;
        }
        stack[++cursor] = i;
    }
    while (cursor != -1) {
        int index = stack[cursor--];
        int left = cursor == -1 ? -1 : stack[cursor];
        int right = n;
        int num = (index - left) * (right - index);
        sum += (num * (long) arr[index]);
        sum %= 1000000007;
    }
    return (int) sum;
}