https://leetcode-cn.com/problems/largest-rectangle-in-histogram/

单调栈

以3为高,左右扩,只能是它自己面积是3.
image.png
下一个数, 1位置的2, 我只要求出左边离我最近的比我小的数在哪,右边离我最近的比我小的数在哪,我就知道我能扩几个刻度了,所以, 以2个格子为高度的面积是4
image.png

把每个数作高都枚举一遍,最大的矩形就是答案,

你可以搞一个单调栈,然后把每个位置的2个信息,全都生成好,然后去结算, 整体O(N)
但是这道题有常数级别的优化,

image.png
image.png

image.png

那如果有相等的呢?也一样,看这个例子
image.png
1 —-> 4弹出之后,真正精彩的来了, 面对相等,我们怎么办?也弹出
image.png
虽然此刻 0—-> 3知道左边的最近的比他小的,但是不知道右边最近的比他小的, 怎么办?依然求个答案
我就认为2位置的3是我扩不到的答案, 所以面积是3 x 2 = 6, 这不是个准确的答案哦
然后把2位置的3进栈
image.png
有朝一日2位置的3可能能求出最优解, 对于重复值,我们每一步可能求得不是准确解,但是不要紧,最后一个不会错过整体最优解,
image.png
这里精彩得地方在于 相等值我也让它弹出, 因为会有后人帮我复仇,也就是等到后人出栈得时候,会为我讨个说法得,所以我现在算得不准确也没关系, 最好得答案最后肯定会抓住,
其实这种思想就是 舍弃部分可能性,但是不影响我最终的答案

(你也可以完全按照单调栈来实现)

正统得单调栈解法是这样得
image.png

  1. // 正统单调栈解法
  2. public int largestRectangleArea(int[] heights) {
  3. int[][] ans = getNearLess(heights);
  4. int maxArea = 0;
  5. for (int i = 0; i < heights.length; i++) {
  6. if (ans[i][1] == -1) {
  7. maxArea = Math.max(maxArea, (heights.length - ans[i][0] - 1 )* heights[i]) ;
  8. } else {
  9. maxArea = Math.max(maxArea, (ans[i][1] - ans[i][0] - 1) * heights[i]) ;
  10. }
  11. }
  12. return maxArea;
  13. }
  14. public int[][] getNearLess(int[] nums) {
  15. int res[][] = new int[nums.length][2];
  16. Stack<List<Integer>> stack = new Stack<>();
  17. for (int i = 0; i < nums.length; i++) {
  18. while (!stack.isEmpty() && nums[stack.peek().get(0)] > nums[i]) {
  19. List<Integer> popIs = stack.pop();
  20. int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
  21. for (Integer popI : popIs) {
  22. res[popI][0] = leftLessIndex;
  23. res[popI][1] = i;
  24. }
  25. }
  26. if (!stack.isEmpty() && nums[stack.peek().get(0)] == nums[i]) {
  27. stack.peek().add(i);
  28. } else {
  29. List<Integer> list = new ArrayList<>();
  30. list.add(i);
  31. stack.push(list);
  32. }
  33. }
  34. while (!stack.isEmpty()) {
  35. List<Integer> popIs = stack.pop();
  36. int leftLessIndex = stack.isEmpty() ? -1 : stack.peek().get(stack.peek().size() - 1);
  37. for (Integer popI : popIs) {
  38. res[popI][0] = leftLessIndex;
  39. res[popI][1] = -1;
  40. }
  41. }
  42. return res;
  43. }

但是此题我们可以让它相等得时候也弹出

  1. public static int largestRectangleArea2(int[] height) {
  2. int maxArea = 0;
  3. Stack<Integer> stack = new Stack<>();
  4. for (int i = 0; i < height.length; i++) {
  5. while (!stack.isEmpty() && height[stack.peek()] >= height[i]) {
  6. int k = stack.pop();
  7. int leftIndexLess = stack.isEmpty() ? -1 : stack.peek();
  8. maxArea = Math.max( maxArea, (i - leftIndexLess - 1) * height[k]);
  9. }
  10. stack.push(i);
  11. }
  12. while (!stack.isEmpty()) {
  13. int k = stack.pop();
  14. int leftIndexLess = stack.isEmpty() ? -1 : stack.peek();
  15. maxArea = Math.max( maxArea, (height.length - leftIndexLess - 1) * height[k]);
  16. }
  17. return maxArea;
  18. }