出自第四章問題24
問題24 直方圖中的最大矩形: 直方圖是由排列在同一基線上的一系列矩形組成的多邊形。爲了簡單起見,假設這些矩形的寬度相等但高度可能不同。例如,下圖1給出了一個直方圖,其中各個矩形的高度爲3、2、5、6、1、4、4,寬度爲標準1單位。當給定了一個保存所有矩形高度的數組時,如何找到其中最大的矩形。對於給定的例子,最大矩形如圖2陰影部分所示:
圖1
圖2
解答:不完整子問題棧的線性搜索: 有很多種方法解決這個問題。Judege給出了一個基於棧求解問題的方法。首先按照從左到右的次序來處理元素,並將已經開始但尚未完成的子直方圖保存在棧中。如果棧爲空,通過將元素壓入棧來開啓一個字問題。否則,將元素與棧頂元素比較。如果新元素較大,那麼將其入棧。如果兩者相等,跳過,處理下一個元素。
如果新元素較小,那麼用棧頂元素更新最大區域,並結束最頂層的子問題,然後刪除棧頂元素,保持當前新元素並重覆上述這個過程(繼續比較)。這樣,所有的子問題都會結束,直至棧爲空,或者棧頂元素小於或等於新元素,導致上述行爲。若所有元素都被處理過而棧仍然不爲空,那麼用棧頂元素更新最大區域來結束剩餘的子問題。
僞代碼如下:
public class StackItem{
public int height;
public int index;
}
int MaxRectangleArea(int A[], int n){
long maxArea = 0;
if(A == null || A.length ==0)
return maxArea;
Stack<StackItem> S = new Stack<StackItem>();
S.push(new Integer.MIN_VALUE, -1);
for(int i = 0; i <= n; i++){
StackItem cur = new StackItem((i < n ? A[i] : Integer.MIN_VALUE), -1);
if(cur.height > S.top().height){
S.push(cur);
continue;
}
while(S.size > 1){
StackItem prev = S.top();
long area = (i - prev.index)*prev.height;
if(area > maxArea)
maxArea = area;
prev.height = cur.height;
if(prev.height > S.get(S.size() - 2).height){
break;
}
s.pop()
}
}
return maxArea;
}
思考: 感覺這個解法有問題,當處理{8,7,6},這樣的遞減數組時,會出錯。下面是我修改後的代碼:
package com.stack;
import java.util.Stack;
public class MaxRectangleArea {
/**
* 直方圖中最大矩形問題
* 主要思路是:將所有數組以非遞減循序存到棧中,當cur小於棧頂元素時,
* 將棧中所有小於cur的值替換成cur,並計算替換的這些值所能組成的最大的矩形面積;
* @param arr
* @return
*/
public static int getMaxRA(int[] arr){
int maxArea = 0;
if(arr == null || arr.length == 0)
return maxArea;
Stack<StackItem> ll = new Stack<StackItem>();
ll.push(new StackItem(Integer.MIN_VALUE, -1));//開頭填入一個最小值看,while循環裏有用
for(int i = 0; i <= arr.length; i++){
//當i=length時說明數組已經遍歷完畢,填入一個最小值使得進入while循環,並求出棧中存儲的數組最大矩形面積
StackItem cur = new StackItem(i < arr.length ? arr[i] : Integer.MIN_VALUE, i);
//當cur當前值不小於棧頂值時,存入棧
if(ll.lastElement().height <= cur.height){
ll.push(cur);
continue;
}
//當cur小於棧頂值prev時,計算prev到cur的矩形面積,並與最大矩形面積maxArea比較,
//拋出棧頂值,直到棧頂值小於當前值cur
int j = 0;
while(ll.size() > 1){
j++;
StackItem prev = ll.pop();
int area = (i - prev.index)*prev.height;
if(area > maxArea)
maxArea = area;
if( ll.lastElement().height < cur.height)
break;
}
//將拋出的值的位置用cur代替存入棧中,保持棧數據不遞減
for(;j >= 0; j--){
ll.push(new StackItem(cur.height, cur.index - j));
}
}
return maxArea;
}
public static void main(String[] args) {
int[] a = {8, 7, 5, 9, 3, 8, 7, 6, 5, 4};
System.out.println("最大矩形面積爲: " + getMaxRA(a));
}
}
class StackItem {
public int height;
public int index;
public StackItem(int height, int index){
this.height = height;
this.index = index;
}
}
結果: