welcome to my blog
LeetCode Top 100 Liked Questions 84. Largest Rectangle in Histogram (Java版; Hard)
题目描述
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.
Example:
Input: [2,1,5,6,2,3]
Output: 10
class Solution {
public int largestRectangleArea(int[] heights) {
int n = heights.length;
if(n==0){
return 0;
}
Stack<Integer> s = new Stack<>();
s.push(-1);
int max = 0;
//
for(int i=0; i<n; i++){
while(s.size()!=1 && heights[i] < heights[s.peek()]){
int j = s.pop();
max = Math.max(max, (i-s.peek()-1)*heights[j]);
}
s.push(i);
}
//栈顶元素一定是heights[n-1]
//
while(s.size()!=1){
int j = s.pop();
max = Math.max(max, (n-s.peek()-1)*heights[j]);
}
return max;
}
}
第一次做, 分治算法; 核心: 最大面积矩形是以下三种情况中的某一种, (1)以最矮柱子为高,向两端扩展的矩形, (2)最矮柱子左边的最大矩形, (3)最矮柱子右边的最大矩形; 我用分治法的时候考虑了这么一种情况, 如果数组只有两个元素, 最小值时靠右的那个, 再次递归时, 有左部分, 但是没有右部分了, 所以我提前判断了是否有左右部分, 不过也可以直接使用if(left>right)这个递归终止条件, 不用提前判断是否有左右部分; 844ms
复杂度分析
时间复杂度:平均开销:O(nlogn); 最坏情况:O(n^2)
如果数组中的数字是有序的,分治算法将没有任何优化效果。
空间复杂度:O(n)。最坏情况下递归需要 O(n) 的空间。
class Solution {
public int largestRectangleArea(int[] heights) {
if(heights==null || heights.length==0)
return 0;
int res = Core(heights, 0, heights.length-1);
return res;
}
public int Core(int[] heights, int left, int right){
//base case
if(left==right)
return heights[left];
int minIndex=left;
for(int i=left+1; i<=right; i++)
minIndex = heights[i] < heights[minIndex] ? i : minIndex;
int curr = (right - left + 1) * heights[minIndex];
int leftArea=0, rightArea = 0;
//如果有左部分的话
if(minIndex-1>=left)
leftArea = Core(heights, left, minIndex-1);
//如果有右部分的话
if(minIndex+1<=right)
rightArea = Core(heights, minIndex+1, right);
return Math.max(Math.max(curr, leftArea), rightArea);
}
}
第一次做, 单调栈:栈底到栈顶递增; 遍历阶段, 清算阶段; 栈中存索引; 初始时栈中压入-1, 为的是计算栈中倒数第二个元素对应的矩形面积; 以弹出的元素对应的高度作为矩形的高度进行计算; 注意遍历阶段和清算阶段面积公式的差别; 和LeetCode32(Hard)很像; 29ms
/*
这是道好题, 和LC32的栈解法有类似的地方, 这两道题的栈解法都需要先往栈中压入一个参考, 方便计算宽度;
区别是LC32会更新参考值, 本题一直以-1作为参考值
实际上也是一种单调栈结构, 具有遍历阶段和清算阶段这两个阶段
*/
import java.util.Stack;
class Solution {
public int largestRectangleArea(int[] heights) {
if(heights==null || heights.length==0)
return 0;
//栈中压入的是索引
Stack<Integer> s = new Stack<>();
//压入参考值, 方便计算宽度
s.push(-1);
//单调栈:栈底到栈顶递增; 包含重复值, 重复值正常入栈就行
//遍历阶段
int res=0, curr, index;
for(int i=0; i<heights.length; i++){
if(s.size()==1)
s.push(i);
else{
if(heights[i] >= heights[s.peek()]){
s.push(i);
}
else{
while(s.size()!=1 && heights[s.peek()] > heights[i]){
//计算以当前柱子高度作为高的矩形的面积
index = s.pop();
curr = (i - s.peek() - 1) * heights[index];
res = Math.max(res, curr);
}
s.push(i);
}
}
}
//清算阶段
//该阶段, 栈中每个索引都满足: 从自己到末尾heights.length-1的范围中, 自己对应的高度最低, 发现这一点非常重要!!
while(s.size()!=1){
index = s.pop();
/*
细节: 括号内减去的是s.peek(), 这里体现了最开始压入-1的作用
具体来说, 输入的高度是{2,1,5,6,2,3},清算阶段,栈中还剩(-1,1,4,5)
弹出5的时候, curr = (heights.length-1 - 4)*heights[5]
弹出4的时候, curr = (heights.length-1 - 1)*heights[4]
弹出1的时候, curr = (heights.length-1 - (-1))*heights[1]
清算阶段,栈中倒数第二个索引对应的高度是所有高度中的最小值, 对应的面积是数组的长度乘栈中倒数第二个元素对应的高度
*/
curr = (heights.length - 1 - s.peek()) * heights[index];
res = Math.max(res, curr);
}
return res;
}
}
题解, 单调栈解法, 写的比我简洁很多
public class Solution {
public int largestRectangleArea(int[] heights) {
Stack < Integer > stack = new Stack < > ();
stack.push(-1);
int maxarea = 0;
for (int i = 0; i < heights.length; ++i) {
while (stack.peek() != -1 && heights[stack.peek()] >= heights[i])
maxarea = Math.max(maxarea, heights[stack.pop()] * (i - stack.peek() - 1));
stack.push(i);
}
while (stack.peek() != -1)
maxarea = Math.max(maxarea, heights[stack.pop()] * (heights.length - stack.peek() -1));
return maxarea;
}
}
题解, 分支算法, 主要注意递归终止条件; 我用分治法的时候考虑了这么一种情况, 如果数组只有两个元素, 最小值时靠右的那个, 再次递归时, 有左部分, 但是没有右部分了, 所以我提前判断了是否有左右部分, 不过也可以直接使用if(left>right)这个递归终止条件, 不用提前判断是否有左右部分
public class Solution {
public int calculateArea(int[] heights, int start, int end) {
if (start > end)
return 0;
int minindex = start;
for (int i = start; i <= end; i++)
if (heights[minindex] > heights[i])
minindex = i;
return Math.max(heights[minindex] * (end - start + 1), Math.max(calculateArea(heights, start, minindex - 1), calculateArea(heights, minindex + 1, end)));
}
public int largestRectangleArea(int[] heights) {
return calculateArea(heights, 0, heights.length - 1);
}
}