diff --git a/README.md b/README.md index 6b3a02f..a6a37e9 100644 --- a/README.md +++ b/README.md @@ -82,6 +82,7 @@ My LeetCode solutions with Chinese explanation. 我的LeetCode中文题解。 | 81 |[Search in Rotated Sorted Array II](https://leetcode.com/problems/search-in-rotated-sorted-array-ii/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/81.%20Search%20in%20Rotated%20Sorted%20Array%20II.md)|Medium| | | 82 |[Remove Duplicates from Sorted List II](https://leetcode.com/problems/remove-duplicates-from-sorted-list-ii/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/82.%20Remove%20Duplicates%20from%20Sorted%20List%20II.md)|Medium| | | 83 |[Remove Duplicates from Sorted List](https://leetcode.com/problems/remove-duplicates-from-sorted-list)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/83.%20Remove%20Duplicates%20from%20Sorted%20List.md)|Easy| | +| 84 |[Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/)|[C++](solutions/84.%20Largest%20Rectangle%20in%20Histogram.md)|Hard| | | 86 |[Partition List](https://leetcode.com/problems/partition-list/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/86.%20Partition%20List.md)|Medium| | | 88 |[Merge Sorted Array](https://leetcode.com/problems/merge-sorted-array)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/88.%20Merge%20Sorted%20Array.md)|Easy| | | 89 |[Gray Code](https://leetcode.com/problems/gray-code/)|[C++](https://github.com/ShusenTang/LeetCode/blob/master/solutions/89.%20Gray%20Code.md)|Medium| | diff --git a/solutions/84. Largest Rectangle in Histogram.md b/solutions/84. Largest Rectangle in Histogram.md new file mode 100644 index 0000000..0fe7107 --- /dev/null +++ b/solutions/84. Largest Rectangle in Histogram.md @@ -0,0 +1,88 @@ +# [84. Largest Rectangle in Histogram](https://leetcode.com/problems/largest-rectangle-in-histogram/) + +# 思路 + +求直方图中面积最大的矩形。 + +## 思路一 +此题比较好想的思路就是从前往后遍历,求出以`heights[i]`为高的最大矩形面积,为此我们需要求出此时最大的宽,该怎样求呢?想象一个木桶,总是最低的那块板子决定桶的装水量。所以我们只需要求出`heights[i]`向左第一个比他小的数`heights[j1]`和向右第一个比他小的数`heights[j2]`,宽就为`j2 - j1 - 1`。 + +求数组中每个元素的左(右)边第一个比他大(小)的元素可利用单调栈在O(n)时间内求出,关于单调栈可以参考[我的总结](../algorithm/array/monotonic_stack_queue.md)。 + +需要至少两次遍历,时间复杂度O(n);空间复杂度O(n) + + +## 思路二 + +此题还有一个比较难想的思路,也是维护一个单调栈,只需要一次遍历。 + +我们考虑寻找到所有可能成为最大矩形的右边界(含)`heights[i]`,这个右边界需要满足一个条件:大于其右边那个元素,即`heights[i] > heights[i+1]`。例如题目例子中的有三个候选右边界2、6、3。为什么要满足这个条件呢?是因为如果`heights[i] <= heights[i+1]`,那么包含`heights[i]`的矩形都可以向右扩展到`heights[i+1]`,所以`heights[i]`不可能成为最大矩形右边界,例如题目例子中的`i=2`时。 + +因为我们向找到`heights[i] > heights[i+1]`的情况,所以我么可以维护一个单调递增的栈,栈里面存放的是下标,具体我们这样处理: +* 若`heights[i]`比栈顶大,直接入栈即可; +* 否则,说明`i-1`就是我们要找的候选右边界(含),右边界有了那左边界和矩形高呢?我们只能不断往左扩展,为此我们不断出栈直到栈空或者`heights[i]`比栈顶大: + * 这个出栈过程中,刚刚出栈的元素对应的height就为矩形的高,此时的栈顶元素设为left就为左边界(不含),所以宽就为`i - 1 - left`(如果栈空的话宽应该是`i`),所以此时的候选矩形面积就可以算出来了。(这里比较难理解,需要结合单调递增栈始终满足的条件:栈顶的元素在原数组中的左边第一个比他小的元素就是次顶元素) + + 最后将`i`入栈。 + + +需要注意的是,由于最后一个板子也是候选右边界,所以这里使用一个小技巧:在heights数组最后面push一个0。 + +只需要遍历一遍,时间复杂度O(n);空间复杂度O(n) + +这个思路还是比较难理解的,需要结合单调递增栈始终满足的特点(栈顶的元素在原数组中的左边第一个比他小的元素就是次顶元素)进行理解。 + + +# C++ +## 思路一 +``` C++ +class Solution { +public: + int largestRectangleArea(vector& heights) { + int n = heights.size(), res = 0; + + vectorpre_smaller(n), next_smaller(n); // 存的是下标 + stackascend_stk1, ascend_stk2; + + for(int i = 0; i < n; i++){ + while(!ascend_stk1.empty() && heights[ascend_stk1.top()] >= heights[i]) + ascend_stk1.pop(); + pre_smaller[i] = ascend_stk1.empty() ? -1 : ascend_stk1.top(); + ascend_stk1.push(i); + + int j = n - i - 1; // 相当于反向遍历: for(int j = n-1; j >= 0; j--) + while(!ascend_stk2.empty() && heights[ascend_stk2.top()] >= heights[j]) + ascend_stk2.pop(); + next_smaller[j] = ascend_stk2.empty() ? n : ascend_stk2.top(); + ascend_stk2.push(j); + } + + for(int i = 0; i < n; i++) + res = max(res, heights[i] * (next_smaller[i] - pre_smaller[i] - 1)); + + return res; + } +}; +``` + +## 思路二 +``` C++ +class Solution { +public: + int largestRectangleArea(vector& heights) { + heights.push_back(0); + int n = heights.size(), res = 0; + + stackascend_stk; + for(int i = 0; i < n; i++){ + while(!ascend_stk.empty() && heights[ascend_stk.top()] >= heights[i]){ + int height = heights[ascend_stk.top()]; ascend_stk.pop(); + int width = ascend_stk.empty() ? i : i - 1 - ascend_stk.top(); + res = max(res, height * width); + } + ascend_stk.push(i); + } + return res; + } +}; +``` \ No newline at end of file