Largest Rectangle in Histogram参考了两种解法,最后用以中间的某点为高度,快速分别求左右边最近的比它低的点为整体思路(编者更新注:推荐直接按此思路实现,因为更容易,ac代码),以单调栈(单调栈用最大/小栈实现,可实现在O(n)时间内获取所有点的左右边最近的更小/大点(严格来说应是左侧的更不大/不小点,右侧的更小/大点)的功能)为实现方式,实现了:
public int largestRectangleArea(int[] heights) {
int max = 0;
Stack<Integer> maxst = new Stack<Integer>() { //简化版最大栈,直观表现为单调不递减数列
{
push(-1);
}
};
for (int i = 0; i <= heights.length; ++i) {
int righth = i == heights.length ? 0 : heights[i];
int midi = maxst.peek();
int midh = midi==-1 ? 0 : heights[midi];
if(righth>=midh) {
maxst.push(i);
}else{
maxst.pop(); //对应方法一的中间值的索引i
int area = midh*(i-maxst.peek()-1); /*i对应方法一的lessFromRight[i], maxst.peek()对应方法一的lessFromLeft[i]*/
max = Math.max(max, area);
--i;
}
}
return max;
}
前种方法可读性强,但是我没明白为什么是O(n),评论里有句“This process of calculate lessFromLeft can be seen as a process of push stack and pop stack, so each element will only be accessed twice.” 但是我也没看明白怎么就像栈的。倒是单调栈的方法,代码更简单,虽然我没看到有谁对它的含义有清晰的解释,但它的意义却可以用前种方法完美地来解释,可见于我在上面代码的注释
trapping-rain-water思路一(推荐):用动态规划快速求得某点的左右两侧的最大值,对应官方解中的Approach 2,ac代码。
思路二:可运用最小栈来实现,思想是已知中间某点,快速分别求左右边最近的比它高的点,ac的代码:(或者)
public int trap(int[] height) {
if(height.length<=2)
return 0;
Stack<Integer> minst = new Stack<Integer>() ;
int i = 0;
for (; i < height.length; ++i) {
if(height[i]!=0) {
minst.push(i);
break;
}
}
int trap = 0;
for (++i; i < height.length; ++i) {
int righth = height[i];
int midi = minst.peek();
int midh = height[midi];
if(righth<=midh) {
minst.push(i);
}else{
minst.pop();
if(!minst.isEmpty()) {
int heightdif = Math.min(righth, height[minst.peek()])-midh;
trap += heightdif*(i-minst.peek()-1);
--i;
}else {
minst.push(i);
}
}
}
return trap;
}
单调递增栈为什么能找左边最近的更小值
Editorial of LeetCode907. Sum of Subarray Minimums :
To build a monotonically increasing stack, as we iterate through an array’s elements, we push them onto the stack. But before pushing an element, we ensure that always increasing property is maintained.
This means if the item at the top of the stack is bigger than or equal to the current item under iteration, we first pop it off before pushing the current element on top.
…
As a new item gets added to the stack, older items are removed from the top if they are bigger. In other words, the items that are getting popped must be greater than or equal to the incoming element. We can also say that the incoming element must be the next smaller element of the item going off the stack. So, every time an item is popped, we get to know about its next smaller item.
If the stack is not empty, the new stack top would contain the previous smaller item. That’s because whenever a new item is added to the stack, we ensure that all the bigger items are removed first. So the stack guarantees that the previous element has to be the previous smaller element.
If the stack becomes empty at the time of removal of an item, it indicates that the outgoing item is the smallest item seen so far. Also note that once the process is complete, the stack contains a series of items sorted in increasing order. These are also the items that have no smaller items after themselves. And their previous smaller items are stored right below them in the stack.
此题的扩展题目:
https://leetcode.com/problems/product-of-array-except-self/
https://leetcode.com/problems/trapping-rain-water-ii/