来源
poj: http://poj.org/problem?id=2082 ;
leetcode: https://leetcode.com/problems/largest-rectangle-in-histogram/
题目:
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 height = [2,1,5,6,2,3],
return 10.
思路:
正常一个方法为:遍历数组中的矩形底,以每个index的底为中心向两边扩张得到以此index为高的最大矩形面积,最后求出全局最大。时间复杂度为O(n^2),空间复杂度为O(1)
单调栈的想法是:维护一个栈,这个栈中的元素保持单调,例如单调递增栈是,只要遍历到的元素大于栈顶元素就压入栈,否则,不断弹出栈顶元素直到当前栈顶元素小于当前遍历元素。
本题解题思路:顺着第一个题的思路,对于每个底边需要向两边扩张找到以它为高的最远边界,那么也就是找到周围都比它高的最远边界。因此,问题就转化为找到一个边左边比它高的边长总和以及右边比它高的边长总和。在单调栈中,当某个元素可以进栈了,说明栈顶元素一定是其左边最高边界(的下一个,或者说第一个比它矮的)。而当一个元素需要出栈时,说明遇到了右边边界(第一个比它矮的)。利用这样的性质,就可以在O(n)的时间内得到结果。
ps:单调栈可以解决需要计算一个元素两边大于它或者两边小于它的边界问题。
@Checked
public int largestRectangleArea(int[] height) {
if(height == null || height.length == 0) return 0;
Deque deque = new ArrayDeque();
int[] area = new int[height.length];
int largest = 0;
for(int i = 0; i < height.length; i++){
//左边没有连续比当前元素大的元素
if(deque.size() == 0 || height[deque.peekLast()] <= height[i]) deque.add(i);
//当有比当前元素大的元素,要弹出比它大的,计算弹出元素右边面积,最后压入当前元素,计算当前元素左边面积。注意相等的情况下
else{
int index = 0;
while(deque.size() != 0 && height[deque.peekLast()] > height[i]){
index = deque.pollLast();
area[index] += (i - index) * height[index];
largest = largest > area[index] ? largest : area[index];
}
index = deque.size() == 0 ? -1 : deque.peekLast();
area[i] += (i - index -1) * height[i];
deque.add(i);
}
}
while(deque.size() != 0){
int index = deque.pollLast();
area[index] += (height.length - index) * height[index];
largest = largest > area[index] ? largest : area[index];
}
return largest;
}
由于此处需要存储左边右边,实际上可以减少存储,在元素弹出时计算其整体面积。为防止后面多加一层循环,可以在数组最后放置一个哨兵。可以简化很多代码
@Checked
public int largestRectangleArea(int[] height) {
if(height == null || height.length == 0) return 0;
Deque deque = new ArrayDeque();
int largest = 0;
for(int i = 0; i <= height.length; i++){
int pivot = i == height.length ? 0 : height[i];
while(deque.size() != 0 && height[deque.peekLast()] > pivot){
int index = deque.pollLast(), pre = deque.size() == 0 ? -1 : deque.peekLast();
int high = (i - pre - 1) * height[index];
largest = largest > high ? largest : high;
}
deque.add(i);//统一操作
}
return largest;
}
poj http://poj.org/problem?id=3250
=
= =
= - = Cows facing right -->
= = =
= - = = =
= = = = = =
1 2 3 4 5 6
Cow#1 can see the hairstyle of cows #2, 3, 4
Cow#2 can see no cow's hairstyle
Cow#3 can see the hairstyle of cow #4
Cow#4 can see no cow's hairstyle
Cow#5 can see the hairstyle of cow 6
Cow#6 can see no cows at all!
每只牛都向右看,遇到比它高的就看不到了,因此找的是比它低的。求所有牛能看到的牛的总和。
本题意义是找到每个底右边比它低的,即使用单调递减栈,每次弹出时计算整个右边边界即可。
public int numbersOfCows(int[] height){
Deque deque = new ArrayDeque();
int count = 0;
for(int i = 0; i <= height.length; i++){
int pivot = i == height.length ? Integer.Max_VALUE : height[i];
while(deque.size() != 0 && pivot > height[deque.peekLast()]){
int index = deque.pollLast();
count += (i - index - 1);
}
deque.add(i);
}
return count;
}
**inspiration:遇到总和各局部周围情况(比它小或者大)求整体最优可以考虑单调栈解法。