单调栈类型总结

Largest Rectangle in Histogram

给出的n个非负整数表示每个直方图的高度,每个直方图的宽均为1,在直方图中找到最大的矩形面积。

Example

样例 1:

输入:[2,1,5,6,2,3]
输出:10
解释:
第三个和第四个直方图截取矩形面积为2*5=10。

思路:求最大矩阵,就是求每个元素height * 最左边第一个小于他的元素 到右边第一个小于他的元素之间的距离;

这样求左右第一个最小,我们用单调递增栈可以完成。注意stack里面存的是index;因为需要计算width,用index可以知道高度;反而用高度没法计算width; Trapping Rain Water 用的是递减栈;这里需要求最小,用的是递增栈;
 

public class Solution {
    /**
     * @param height: A list of integer
     * @return: The area of largest rectangle in the histogram
     */
    public int largestRectangleArea(int[] height) {
        // 需要找到每个元素对应两边第一个最小的元素;
        // 这题跟Trapping Rain Water 是反着的,这里需要用单调递增栈;
        if(height == null || height.length == 0) return 0;
        Stack<Integer> stack = new Stack<Integer>();
        int max = 0;
        for(int i = 0;  i <= height.length; i++){
            // -1是为了把栈里面所有的元素都弹出来计算的;1,2,3,4,5,-1
            int item = (i == height.length) ? -1 : height[i];
            
            while(!stack.isEmpty() && item <= height[stack.peek()]){
                int h = height[stack.pop()]; // 踢出来元素的高度;
                int left = stack.isEmpty() ? 0 : stack.peek() + 1; // 左边第一个小于他的下一个
                int right = i-1; // 右边第一个小于他的前一个;
                int area = h * (right - left + 1); //计算可能的矩阵;
                max = Math.max(max, area);
            }
            stack.push(i);
        }
        return max;
    }
}

Maximal Rectangle

给你一个二维矩阵,权值为FalseTrue,找到一个最大的矩形,使得里面的值全部为True,输出它的面积

Example

样例1

输入:
[
  [1, 1, 0, 0, 1],
  [0, 1, 0, 0, 1],
  [0, 0, 1, 1, 1],
  [0, 0, 1, 1, 1],
  [0, 0, 0, 0, 1]
]
输出: 6

 思路:把上面一题,稍微变化一下,求每个row的histgram,就可以O(n^2)来做这题;

public class Solution {
    /**
     * @param matrix: a boolean 2D matrix
     * @return: an integer
     */
    public int maximalRectangle(boolean[][] matrix) {
        if(matrix == null || matrix.length == 0 || matrix[0].length == 0) {
            return 0;
        }
        int m = matrix[0].length;
        int[] height = new int[m];
        
        int maxarea = 0;
        for(int i = 0; i < matrix.length; i++) {
            for(int j = 0; j < matrix[0].length; j++) {
                if(i == 0) {
                    height[j] = matrix[0][j] == false ? 0 : 1;
                } else {
                    height[j] = matrix[i][j] == false ? 0 : height[j] + 1;
                }
            }
            maxarea = Math.max(maxarea, largestRectangleArea(height));
        }
        return maxarea;
    }
    
    private int largestRectangleArea(int[] height) {
        if(height == null || height.length == 0) {
            return 0;
        }
        
        Stack<Integer> stack = new Stack<Integer>();
        int maxarea = 0;
        for(int i = 0; i <= height.length; i++) {
            int curt = (i == height.length) ? -1 : height[i];
            while(!stack.isEmpty() && curt <= height[stack.peek()]){
                int h = height[stack.pop()];
                int left = stack.isEmpty() ? 0 : stack.peek() + 1;
                int right = i - 1;
                int area = h * (right - left + 1);
                maxarea = Math.max(maxarea, area);
            }
            stack.push(i);
        }
        return maxarea;
    }
}

Trapping Rain Water

给出 n 个非负整数,代表一张X轴上每个区域宽度为 1 的海拔图, 计算这个海拔图最多能接住多少(面积)雨水。

思路:需要找到每个元素从左右两边看,左右两边第一个最大的元素,water 就是左右两边最大的最小值 - 当前的height;

扫描二维码关注公众号,回复: 8710266 查看本文章

这样用单调递减栈,注意stack里面存的是index,用来求宽度的;

public class Solution {
    /**
     * @param heights: a list of integers
     * @return: a integer
     */
    public int trapRainWater(int[] heights) {
        if(heights == null || heights.length == 0) {
            return 0;
        }
        Stack<Integer> stack = new Stack<Integer>();
        int water = 0;
        for(int i = 0; i < heights.length; i++) {
            while(!stack.isEmpty() && heights[stack.peek()] <= heights[i]){
                int j = stack.pop();
                if(!stack.isEmpty()){
                    int h = Math.min(heights[stack.peek()], heights[i]) - heights[j];
                    int right = i - 1;
                    int left = stack.peek() + 1;
                    int w = right - left + 1;
                    water += w * h;
                }
            }
            stack.push(i);
        }
        return water;
    }
}

Max Tree

给出一个没有重复的整数数组,在此数组上建立最大树的定义如下:

  • 根是数组中最大的数
  • 左子树和右子树元素分别是被父节点元素切分开的子数组中的最大值

利用给定的数组构造最大树。

Example

样例 1:

输入:[2, 5, 6, 0, 3, 1]
输出:{6,5,3,2,#,0,1}
解释:
此数组构造的最大树是:
    6
   / \
  5   3
 /   / \
2   0   1

思路:用单调递减栈,因为L, <x, <x....X, <x, <x, R, (L>X, R>X)  X能够给他父亲的,只能是L , R中的一个. 所以,这题变成了找左右两边第一个比X大的点,取最小值,然后分别接到左右(看X是连接在L,R的左边还是右边); 

/**
 * Definition of TreeNode:
 * public class TreeNode {
 *     public int val;
 *     public TreeNode left, right;
 *     public TreeNode(int val) {
 *         this.val = val;
 *         this.left = this.right = null;
 *     }
 * }
 */

public class Solution {
    /**
     * @param A: Given an integer array with no duplicates.
     * @return: The root of max tree.
     */
    public TreeNode maxTree(int[] A) {
        if(A == null || A.length == 0) {
            return null;
        }
        
        Stack<TreeNode> stack = new Stack<TreeNode>();
        for(int i = 0; i <= A.length; i++) {
            TreeNode node = (i == A.length) ? new TreeNode(Integer.MAX_VALUE) 
                                            : new TreeNode(A[i]);
            while(!stack.isEmpty() && stack.peek().val <= node.val){
                TreeNode curnode = stack.pop();
                //核心就是安排pop出来的点,是去L,还是R的左边还是右边;
                // 注意因为上面pop了,这里一定要判断一下stack是否为空;
                if(!stack.isEmpty() && node.val > stack.peek().val) {
                    stack.peek().right = curnode;
                } else {
                    // node.val <= stack.peek().val;
                    node.left = curnode;
                }
            }
            stack.push(node);
        }
        return stack.pop().left;
    }
}
发布了562 篇原创文章 · 获赞 13 · 访问量 17万+

猜你喜欢

转载自blog.csdn.net/u013325815/article/details/103946328