原题
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.
Above is a histogram where width of each bar is 1, given height =
[2,1,5,6,2,3]
.The largest rectangle is shown in the shaded area, which has area =
10
unit.
示例
Given heights =
[2,1,5,6,2,3]
,
return10
.
解题思路
第一阶段:
拿到这种题,首先想怎么用最暴力的方法解决这个问题,于是自然而然的想出来枚举的方法,即利用二重循环枚举所有可能的区间,接着再这区间中找出最小的值,然后用(最小值 * 区间宽度)求出每个区间的最大值,最后从这些最大值中找出最大解。
这种解法的时间复杂度是O(n^3),一般都会超时的,那么我们就会想着降低时间复杂度,即去冗余的方法(冗余分为重复计算和不需要计算两种)
第二阶段:
那么哪里存在冗余呢?第一个想到的应该是区间内求最小值时存在重复计算,我们可以使用空间换时间的方法将之前算过的最小值存储下来,这样求最小值[i][j] 就等于MIN( 最小值[i][j-1] , j) ,这样我们就时间复杂度降低到了O(n^2)
第三阶段:
通过推演又进一步发现了冗余的地方,比方说对于[9, 8, 10, 3, 12, 11,15]来说,虽然我们枚举了所有可能的区间,计算了其区间内的最小值,但你有没有发现,其中大多数的最小值都是 3, 那么我们能不能改变思路减少该冗余呢?
于是乎我们可以采用枚举最小值的算法,即假设每一个点都是最小值,然后我们计算出其作为最小值可以向左右两边延伸的最大区间(如果该点左边的点值大于该点,则继续往左比较,知道某点比该点值小为止)
最后可以算出每一个点作为最小值所包括的最大面积。面积 = n点高度 * (n右边界 - n左边界 + 1 )
第四阶段:
上一阶段的时间复杂度似乎并没有减少,那么肯定这种新思路引来了新的冗余,这种冗余在哪里呢?
我们发现在找一个点的左右最大区间时存在重复计算,因为如果n点比n - 1点的值大的话,那么n点左边界应该大于等于n - 1点的左边界,于是乎我们可以存储下每个点的左右边界,避免很多重复计算
最终思路:
- 枚举所有点,将其作为最小值
- 记录每个点的左右边界(计算n点左边界的方法是:判断n是否比n - 1小,如果成立则跳到n - 1 点的左边界x, 比较n是否比x小,如此循环,知道求出左边界)
- 枚举每一个点的最大面积,计算最大解
完整代码
public class Solution { public int largestRectangleArea(int[] heights) { int n = heights.length; if (n == 0) { return 0; } // 求左边的边界 int[] left = new int[n]; left[0] = 0; for (int i=1;i<n;++i) { int A = i; while (A > 0 && heights[A - 1] >= heights[i]) { A = left[A - 1]; } left[i] = A; } // 求右边的边界 int[] right = new int[n]; right[n - 1] = n - 1; for (int i=n-2;i>=0;--i) { int A = i; while (A < n - 1 && heights[A + 1] >= heights[i]) { A = right[A + 1]; } right[i] = A; } // 枚举每一个最小值的最大面积 int ans = 0; for (int i=0;i<n;++i) { ans = Math.max(ans, heights[i] * (right[i] - left[i] + 1)); } return ans; } }