单调栈

137 阅读1分钟

题目一

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

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<Integer>();
   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;
}

题目二

给定一个非负数组arr,代表直方图 返回直方图的最大长方形面积

public static int largestRectangleArea1(int[] height) {
   if (height == null || height.length == 0) {
      return 0;
   }
   int maxArea = 0;
   Stack<Integer> stack = new Stack<Integer>();
   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;
}

public static int largestRectangleArea2(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;
}

题目三

给定一个二维数组matrix,其中的值不是0就是1, 返回全部由1组成的最大子矩形,内部有多少个1

public static int maximalRectangle(char[][] map) {
   if (map == null || map.length == 0 || map[0].length == 0) {
      return 0;
   }
   int maxArea = 0;
   int[] height = new int[map[0].length];
   for (int i = 0; i < map.length; i++) {
      for (int j = 0; j < map[0].length; j++) {
         height[j] = map[i][j] == '0' ? 0 : height[j] + 1;
      }
      maxArea = Math.max(maxRecFromBottom(height), maxArea);
   }
   return maxArea;
}

// height是正方图数组
public static int maxRecFromBottom(int[] height) {
   if (height == null || height.length == 0) {
      return 0;
   }
   int maxArea = 0;
   Stack<Integer> stack = new Stack<Integer>();
   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;
}

题目四

给定一个二维数组matrix,其中的值不是0就是1, 返回全部由1组成的子矩形数量

public static int numSubmat(int[][] mat) {
    if (mat == null || mat.length == 0 || mat[0].length == 0) {
        return 0;
    }
    int nums = 0;
    int[] height = new int[mat[0].length];
    for (int i = 0; i < mat.length; i++) {
        for (int j = 0; j < mat[0].length; j++) {
            height[j] = mat[i][j] == 0 ? 0 : height[j] + 1;
        }
        nums += countFromBottom(height);
    }
    return nums;

}

// 比如
//              1
//              1
//              1         1
//    1         1         1
//    1         1         1
//    1         1         1
//             
//    2  ....   6   ....  9
// 如上图,假设在6位置,1的高度为6
// 在6位置的左边,离6位置最近、且小于高度6的位置是2,2位置的高度是3
// 在6位置的右边,离6位置最近、且小于高度6的位置是9,9位置的高度是4
// 此时我们求什么?
// 1) 求在3~8范围上,必须以高度6作为高的矩形,有几个?
// 2) 求在3~8范围上,必须以高度5作为高的矩形,有几个?
// 也就是说,<=4的高度,一律不求
// 那么,1) 求必须以位置6的高度6作为高的矩形,有几个?
// 3..3  3..4  3..5  3..6  3..7  3..8
// 4..4  4..5  4..6  4..7  4..8
// 5..5  5..6  5..7  5..8
// 6..6  6..7  6..8
// 7..7  7..8
// 8..8
// 这么多!= 21 = (9 - 2 - 1) * (9 - 2) / 2
// 这就是任何一个数字从栈里弹出的时候,计算矩形数量的方式
public static int countFromBottom(int[] height) {
    if (height == null || height.length == 0) {
        return 0;
    }
    int nums = 0;
    int[] stack = new int[height.length];
    int si = -1;
    for (int i = 0; i < height.length; i++) {
        while (si != -1 && height[stack[si]] >= height[i]) {
            int cur = stack[si--];
            if (height[cur] > height[i]) {
                int left = si == -1 ? -1 : stack[si];
                int n = i - left - 1;
                int down = Math.max(left == -1 ? 0 : height[left], height[i]);
                nums += (height[cur] - down) * num(n);
            }

        }
        stack[++si] = i;
    }
    while (si != -1) {
        int cur = stack[si--];
        int left = si == -1 ? -1 : stack[si];
        int n = height.length - left - 1;
        int down = left == -1 ? 0 : height[left];
        nums += (height[cur] - down) * num(n);
    }
    return nums;
}

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

题目五

给定一个数组arr,返回所有子数组最小值的累加和
leetcode.com/problems/su…

public static int sumSubarrayMins(int[] arr) {
    int N = arr.length;
    int[] stack = new int[N];
    int[] left = new int[N];
    int[] right = new int[N];
    int si = -1;
    for (int i = 0; i < N; i++) {
        // 大于号表示:相等的时候,右边界可以算对,左边界是上一个相等的数
        while (si != -1 && arr[stack[si]] > arr[i]) {
            int cur = stack[si--];
            int l = si == -1 ? -1 : stack[si];
            left[cur] = l;
            right[cur] = i;
        }
        stack[++si] = i;
    }
    while (si != -1) {
        int cur = stack[si--];
        int l = si == -1 ? -1 : stack[si];
        left[cur] = l;
        right[cur] = N;
    }

    long ans = 0;
    for (int i = 0; i < arr.length; i++) {
        long start = i - left[i];
        long end = right[i] - i;
        ans += start * end * (long) arr[i];
        ans %= 1000000007;
    }
    return (int) ans;
}