每天一道LeetCode-----计算直方图中最大矩形的面积
Largest Rectangle in Histogram
原題鏈接Largest Rectangle in Histogram
給定一個直方圖,計算這個直方圖中最大的矩形面積。輸入的是直方圖中每個柱的高度
首先可以想到要求面積就需要確定矩形的高度,但是每個柱子的高度是不一樣的,而不同的柱子組成的矩形要以最短的那個作為高
那么,是否可以對每個高度的分別計算面積呢?簡單的說就是以每個柱子的高度作為最后矩形的高度,計算形成的最大矩形的面積
隱含的方法就是對于每個柱子,它的高度記為H,需要
- 向左找第一個高度小于H的柱子位置,下標記為l
- 向右找第一個高度小于H的柱子位置,下標記為r
那么,以H作為高的矩形的寬為(r - l - 1),面積為(r - l - 1) * H
只需要把所有這樣的面積求出來取最大值即可
代碼如下
class Solution { public:int largestRectangleArea(vector<int>& heights) {int maxArea = 0;for(int i = 0; i < heights.size(); ++i){/* 向左找第一個小于當前高度的位置 */int l = i - 1;while(l >= 0 && heights[l] >= heights[i])--l;/* 向右找第一個小于當前高度的位置 */int r = i + 1;while(r < heights.size() && heights[r] >= heights[i])++r;/* 計算面積 */maxArea = std::max(maxArea, (r - l - 1) * heights[i]);}return maxArea;} };但是,這種方法超時了:(
可以想到時間主要都消耗在內部的兩個while循環,其實循環目的沒錯,為了尋找矩形的左右邊界,但是試想,如果heights中所有元素都相等(極端情況),那么每次while循環可能都需要遍歷整個heights直到l < 0或者r >= heights.size()
可以從這兩方面入手,考慮如何優化兩個while循環
先考慮尋找左邊界時的優化
假設需要找heights[i]的左邊界,根據上述方法,需要一次遍歷
i-1, i-2, ..., k1找到第一個k1滿足
heights[k1] < heights[i]在下次循環中(i = i + 1),又需要找heights[i+1]的左邊界,同樣的遍歷方法
i, i-1, i-2, ..., k2找到第一個k2滿足
heights[k2] < heights[i+1]仔細觀察這兩次循環,實際上是存在重復部分的,在找heights[i+1]的左邊界過程中,假設有
heights[i] >= heights[i+1]那么下次其實不需要從i-1開始繼續比較heights[i-1] >= heights[i+1],因為當遍歷i+1時,有些東西是已知的,那就是
- 第一個小于heights[i]的位置,即k1
由于heights[i] >= heights[i+1],從i, i-1, …, k+1這些位置上的元素一定也都大于等于heights[i+1],那么為什么不從k1開始比較呢,是吧:)
當然這種方法是建立在對heights的遍歷是從左向右的,即
for(int i = 0; i < heights.size(); ++i)...//尋找heights[i]的左邊界對于右邊界,就需要讓heights的遍歷從右向左
for(int i = heights.size() - 1; i >= 0; --i)...//尋找heights[i]的右邊界最后再來一次循環,但是這次heights[i]的左邊界和右邊界都是已知的
class Solution { public:int largestRectangleArea(vector<int>& heights) {if(heights.empty())return 0;int n = heights.size();vector<int> L(n, 0);vector<int> R(n, 0);for(int i = 0; i < n; ++i){int l = i - 1;while(l >= 0 && heights[l] >= heights[i])l = L[l]; //直接從k1開始找L[i] = l;}for(int i = n - 1; i >= 0; --i){int r = i + 1;while(r < n && heights[r] >= heights[i])r = R[r]; //直接從k1開始找R[i] = r;}int maxArea = 0;for(int i = 0; i < n; ++i){//L[i]表示左邊第一個小于heights[i]的位置//R[i]表示右邊第一個小于heights[i]的位置maxArea = std::max(maxArea, (R[i] - L[i] - 1) * heights[i]);}return maxArea;} };當然啦,如果覺得for循環有點多,可以把后兩個合在一起:)
class Solution { public:int largestRectangleArea(vector<int>& heights) {if(heights.empty())return 0;int n = heights.size();vector<int> L(n, 0);vector<int> R(n, 0);for(int i = 0; i < n; ++i){int l = i - 1;while(l >= 0 && heights[l] >= heights[i])l = L[l]; //直接從k1開始找L[i] = l;}int maxArea = 0;for(int i = n - 1; i >= 0; --i){int r = i + 1;while(r < n && heights[r] >= heights[i])r = R[r]; //直接從k1開始找R[i] = r;maxArea = std::max(maxArea, (R[i] - L[i] - 1) * heights[i]);}return maxArea;} };其實通常首先想到的就是最開始那個一個for循環,內部兩個while找左右邊界的版本,但是實際上while循環是可以優化的,如果知道優化的方法(找到重復計算的原因),效率會高很多
總結
以上是生活随笔為你收集整理的每天一道LeetCode-----计算直方图中最大矩形的面积的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 每天一道LeetCode-----在字符
- 下一篇: 每天一道LeetCode-----将链表