84. Largest Rectangle in Histogram
Total Accepted: 57808 Total Submissions: 239940 Difficulty: 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.
For example,
Given heights = [2,1,5,6,2,3]
,
return 10
.
42. Trapping Rain Water
Total Accepted: 63876 Total Submissions: 199058 Difficulty: Hard
Given n non-negative integers representing an elevation map where the width of each bar is 1, compute how much water it is able to trap after raining.
For example,
Given [0,1,0,2,1,0,1,3,2,1,2,1]
, return 6
.
The above elevation map is represented by array [0,1,0,2,1,0,1,3,2,1,2,1]. In this case, 6 units of rain water (blue section) are being trapped. Thanks Marcos for contributing this image!
【分析】
这个题之前我在一次面试中曾被问及,当时没有给出好的解法,没想到出处在此。此题与【LeetCode】42. Trapping Rain Water有一定相似,但算法要难一些。
对于此题(84),最直观的思路就是列举所有可能的矩形组合,共有n(n+1)/2个组合,时间复杂度为O(n2),这样的"暴力解法"自然过不了大集合测试,超时是必然的。我在网上看到一个精妙的解法,但是并不直观,我根据其核心思想,自己写了一个C++的解法:
1、核心规律:当给定直方图高度为“升序”排列的时候,如[1,5,6,8],我们只需要比较1*4, 5*3, 6*2, 8*1的大小,找出最大值即可,一次遍历,O(n)即可完成;
2、异常处理:当给定的直方图出现局部“降序”排列时,如[2,3,1,5,6,8],由于heights[2]<heights[1],heights[1]与它后面的直方图构成的矩形将受到heights[2]的约束,即高度被限制在了heights[2],这种情况我们将降序点前面的升序部分作为一个子问题先行求取面积,即计算[2.3]可构成的矩形的面积:2*2,3*1,并和全局最大值比较,如果更大则保存,此后由于上述约束,我们可以将序列转换成:[1,1,1,5,6,8],这样形成一个准“升序”,按这种方法处理,直到遍历整个数组(直方图高度数组),最后将得到一个准“升序”的序列;
3、对最后形成的“升序”序列求面积:若序列长度为N,面积分别为:heights[0]*N,heights[1]*(N-1),...,heights[N-1]*1;将这些面积与全局最大值比较,最后得到的最大面积为全局最大;
4、对于算法来说,合适的数据结构可以简化操作,根据上面分析,一种合适的数据结构是栈,利用栈可以很容完成上面的算法;
【解法及注释】
class Solution { public: int largestRectangleArea(vector<int>& heights) { if(heights.size()==0)return 0;//空输入处理 int maxArea=0;//存储最大面积 int count=0;//计数变量,记录当前入栈数据个数 stack<int> stk;//定义一个辅助栈 int i=0; while(i<heights.size())//循环遍历,这个地方用for循环亦可 { if(stk.empty()||stk.top()<=heights[i])//如果栈为空或者数据为升序排列,则依次入栈 { stk.push(heights[i]); i++; count++;//记录入栈数据个数,便于计算面积 } else//出现局部降序,处理 { int k=0; while(k<count&&stk.top()>heights[i])//求取降序点前的升序部分直方图可组合的矩形面积,k约束循环次数 { k++; int top=stk.top();//比降序点大的栈顶元素依次出栈并求取面积,这里是个技巧,可以避免不必要的计算 stk.pop(); maxArea=Max(maxArea,top*k); } while(k--)//将降序点前面比降序点数值大的部分“铲平”,置为降序点的数值,并入栈 { stk.push(heights[i]); } } } int count1=0; while(count1<count)//遍历完成,最后形成一个准“升序”序列,栈顶数据依次出栈求取组合面积,继续寻找最大值 { count1++; int top=stk.top(); stk.pop(); maxArea=Max(maxArea,top*count1); } return maxArea; } private: int Max(int a, int b){return a>b?a:b;} };
【运行结果】