leetcode: Largest Rectangle in Histogram,Maximal Square,Maximal Square问题

来源:互联网 发布:英汉词典软件下载 编辑:程序博客网 时间:2024/05/21 19:34

Largest Rectangle问题

题目描述

Given n non-negative integers representing the histogram’s bar height where the width of each bar is 1, find the area of largest rectangle in the histogram.

主要思路

leetcode上主要的答案集中在: http://www.geeksforgeeks.org/largest-rectangle-under-histogram/。
查阅了很多博客,这篇对于原理阐述的比较好:http://www.tuicool.com/articles/NnqYBj
核心原理就是:选择合适的时机回探,避免重复的回探。
本人根据这里的描述,加上了自己的理解。
最关键的一点是:每个最大矩形区域的两边的小矩形,必然小于区域中任意一个矩形。比如下图:
这里写图片描述

假如区域两边的矩形不是1和2,而是5,6或者更大,那么区域必然也可以扩展,从而可以得到更大的区域。

这样一来,我们可以根据区域右边必然有一个小于区域中所有矩形的矩形这一结论,来查找所有的情况。我们把右边这个最小的矩形成为 “小R”

我们可以用栈维护一个递增序列,当当前元素小于栈顶元素时,取出栈顶元素进行回探。栈中一直维护着一个递增的序列,当出现一个元素比栈顶元素小时,我们对栈中元素进行回探。同时,弹出已经回探的元素避免多次回探。

具体步骤也就是,利用一个栈,栈中存放高度递增的矩形的序号,当一个矩形比栈顶矩形高度更高,把下标推入栈。注意,为了枚举全部情况,我们把图中高度为3的矩形右边虚拟一个高度为0的矩形。
当找到一个矩形小于栈顶矩形,那么我们把它看成小R,也就是假设最大区域出现在小R右边,那么把栈顶大于小R的矩形依次弹出,不断计算矩形的面积即可。直到栈顶的元素比 小R 还小,那么这个元素必然不在结果区域中,否则,加上小R实际上可以形成更大的结果区域。也就是说,栈中的元素如果小于小R了,那么可以留在以后回探。
除此之外,每次从栈中拿出元素回探后,回探过的矩形可以出栈,避免重复的回探。因为栈中存放的是下标,同时矩形高度递增,所以最后区域的高度由栈中剩下较矮的矩形决定,从而可以从栈中探出回探过的矩形。

这里写图片描述

计算小R左边栈中比它大的元素的和过程如上图,标蓝为两轮计算过程中的区域。

可以看出,我们其实是在回探小R左边比小R大的矩形,看有没有可能组成更大的矩形。这样,我们在最右边补偿一个虚拟矩形0,可以做到从最右边将全部矩形可能的组合回探一遍。为了计算小R左边组合矩形的面积,我们需要先弹出栈顶的矩形A,然后获取当前新的栈顶的矩形B下标,来获得组合矩形的宽。注意栈一直存放的是自增的元素,所以B可能可A不是相邻的,但A和B之间的矩形肯定是之前从栈中弹出的,即比A更大,那么BXXXA这个组合矩形的最小高为A。

代码如下

public class Solution {    public int largestRectangleArea(int[] heights) {        int len = heights.length;        Stack<Integer> stack = new Stack<>();        int max = 0;        for (int i = 0;i<=len;i++) {            //右边补偿了一个高为0的虚拟矩形。            int h = (i == len ? 0 : heights[i]);            if (stack.isEmpty() || heights[stack.peek()] <= h) {            //栈为空或者大于栈顶元素,把索引入栈。                stack.push(i);            }else {                //弹出比小R大的元素。                int index = stack.pop();                //栈为空,表明左边的矩形都比当前栈顶的矩形高,区域取当前矩形为最矮计算即可。                //栈不为空,栈顶元素为要计算的区域的左边界。                //注意,比栈中元素大的元素都已弹出,所以从stack.peek()到i之间的元素都大于heights[index],从而可以使用乘法计算矩形面积                max = Math.max(max,heights[index]*(stack.isEmpty()? i:(i - stack.peek() - 1)));                //i--,下次还将栈顶元素和小R比较。                i--;            }        }        return max;    }}

Maximal Square问题

求二维数组中1组成的最大矩形面积,比如:
1 0 1 0 0
1 0 1 1 1
1 1 1 1 1
1 0 0 1 0
面积为6.

题目和上面的题其实可以用同一种思路求解。
首先逐行去看,第一行10100,将列看成下标,可以得到一排矩形:
这里写图片描述
我们利用上面的Larget Rectangle算法求出当前最大矩形,然后读第二行,这里注意:
如果第j列元素为1,则加上之前相应位置的值,类似得到一个更高的矩形:
于是第一、二行可以组成矩形如下:
这里写图片描述
注意,如果是0的话,那么将相应列重置为0。
第三行:
这里写图片描述
这样,每一行依次组成矩形,并在所有矩形中找出最大的组合矩形,即为结果:
代码如下:

public class Solution {    public int maximalRectangle(char[][] matrix) {        if(matrix.length == 0) return 0;        int[] H = new int[matrix[0].length];        int r = 0;        for (int i = 0;i<matrix.length;i++) {            for (int j = 0;j<matrix[0].length;j++) {                if (matrix[i][j] == '1') H[j]++;                else H[j] = 0;            }            r = Math.max(r, largestRectangleArea(H));        }        return r;    }

可以看到,我们用一个H表示当前行的矩形高,纵坐标就是矩形坐标,然后调用largestRectangleArea的算法即可。


Maximal Square问题

这道题是第二道题的一个变种,

找出一个二维数组中最大包含1的正方形。

我们先思考一下暴力的解法,可以取二维数组中每一个元素作为正方形的右下角元素,找出包含该元素的最大正方形即可。

大概就是遍历每个元素,然后向每个元素左上方去寻找一个最大的正方形。
这样一来,因为每个大正方形可以由左上方的小正方形进化而来,我们可以利用DP求解。

取一个二维数组dp,dp[i][j]为包含点i,j的左上方的最大正方形的边长。首先对于dp数组的第一行和第一列,可以看到一旦有1出现,那么就该位置的值就是1,因为左上方不存在正方形了。这样我们可以进行初始化操作。

然后对于一个dp[i][j],它和左上方部分组成一个更大的正方形应该如何组成?看一下下面这张图:
这里写图片描述
很明显,如果i,j位置元素值为1,那么它可以组成一个边长为1的正方形,和左上方元素组成的正方形的边长应该是如图蓝色区域的正方形边长。
这里写图片描述

也就是递推关系:dp[i][j] = min(dp[i-1][j],dp[i][j-1],dp[i-1][j-1])
依次可以写出dp的代码:

public class Solution {    public int maximalSquare(char[][] matrix) {        if (matrix.length == 0) return 0;        int max = 0;        //dp的值表示包含点i,j的最大正方形的边长        int[][] dp = new int[matrix.length][matrix[0].length];        for (int i = 0;i<matrix.length;i++) {            if (matrix[i][0] == '1'){                dp[i][0] = 1;                max = 1;               }        }        for (int j = 0;j<matrix[0].length;j++) {            if (matrix[0][j] == '1'){                dp[0][j] = 1;                max = 1;               }        }        for (int i = 1;i<matrix.length;i++) {            for (int j = 1;j<matrix[0].length;j++) {                if (matrix[i][j] == '1')                    dp[i][j] = Math.min(dp[i-1][j],Math.min(dp[i][j-1],dp[i-1][j-1])) + 1;                max = Math.max(dp[i][j] * dp[i][j], max);            }        }        return max;    }}
0 0
原创粉丝点击