單調棧
1. 單調棧的結構
背景:給定一個數組,求數組中每一個元素的左邊距離其最近的比它小的值,和右邊距離其最近的比它小的值。
數組:
arr:{3,5,4,2,6}
arr[i] left right 3 null 2 5 3 4 4 3 2 2 null null 6 2 null
思路:設計一個棧,依次存入的元素大於等於棧頂元素,保持棧中元素從下往上逐漸增大。
- 如果要存入的元素
num
大於棧頂,則直接入棧。 - 如果要存入的元素
num
等於棧頂,則一起存入棧頂(棧頂中的每一個元素可以用鏈表實現)。 - 如果要存入的元素
num
小於棧頂,則彈出棧頂元素peek
,則num
爲peek
的右邊距離其最近且小於它的元素。當彈出peek
後,此時的棧頂元素peekCur
爲peek
的左邊距離其最近且小於它的元素。 - 當遍歷完數組,棧不爲空的話,依次彈出棧頂元素
peek
,此時該peek
的右邊距離其最近且小於它的元素爲null,左邊距離其最近且小於它的元素爲下一個棧頂元素。
2. 最大拼接的面積
題目:有如下矩形圖,求該矩形圖中能拼接成矩形的最大面積。
思路:用單調棧判斷每個矩形左右兩邊,距離其最近,且比其矮的矩形。
題解:
- 黃色矩形高爲5,由於右邊棕色矩形比黃色矩形高,所以黃色矩形可以和棕色矩形拼接成高爲5,寬爲2的矩形。
- 由於棕色矩形左右兩邊的矩形都比它低,所以它能拼接成的矩形是他自己。高爲6,寬爲1。
- 橙色矩形最低,能拼接所有矩形,最終形成一個高爲3,寬爲5的矩形。
- 藍色矩形也不能和其他矩形拼接,高爲7,寬爲1。
- 綠色矩形能和藍色矩形拼接,形成高爲4,寬爲2的矩形。
3. 求最大子矩陣的大小
題目:給定一個整型矩陣map, 其中的值只有0 和 1 兩種, 求其中全是1 的所有矩形區域中, 最大的矩形區域爲1的數量。
例:
1 0 1 1
1 1 1 1
1 1 1 0其中,最大的矩形區域有6個1,所以返回6 。
思路:把矩陣的每一行看成矩形圖。
- 如第一行
[1,0,1,1]
,把它看成矩形圖,由四個矩形組成,高分別爲1,0,1,1,寬都爲1。求矩形最大拼接面積。 - 第二行
1,1,1,1
和第一行[1,0,1,1]
組合起來,組成一個矩形圖,四個矩形高分別2,1,2,2,寬都爲1。求矩形最大拼接面積。 - 最後一行把整個矩陣看成一個矩形圖,注意,四個矩形高分別爲3,2,3,0。求矩形最大拼接面積。
- 得到的矩形最大拼接面積的最大值即爲結果。
代碼:
import java.util.Stack;
class Solution_MaxArea{
public static int maxAreaMatrix(int[][] arr){
//判斷矩陣是否爲空
if(arr == null || (arr.length == 1 && arr[0].length == 0) || arr.length == 0){
return 0;
}
int result = Integer.MIN_VALUE;
int[] rectangle = new int[arr[0].length];
for(int i = 0; i < arr.length; i++){
for(int j = 0; j < arr[i].length; j++){
rectangle[j] = arr[i][j] == 0 ? 0 : rectangle[j] + arr[i][j];
}
result = Math.max(maxAreaRectangle(rectangle), result);
}
return result;
}
public static int maxAreaRectangle(int[] arr){
if(arr == null || arr.length == 0){
return 0;
}
int len = arr.length;
Stack<Integer> stack = new Stack<>();
int res = Integer.MIN_VALUE;
//哪個元素先彈出,先計算以其代表的矩形的高爲最大矩形的高,在遍歷到的所有矩形中能合併成的最大矩形 的面積
for(int i = 0; i < len; i++){
//注意,是大於等於號。
while(!stack.isEmpty() && arr[stack.peek()] >= arr[i]){
int height = arr[stack.pop()];
int left = stack.isEmpty() ? -1 : stack.peek();
int right = i;
int area = height * (right - left - 1);
res = Math.max(area, res);
}
stack.push(i);
}
while(!stack.isEmpty()){
int height = arr[stack.pop()];
int right = len;
int left = stack.isEmpty() ? -1 : stack.peek();
int area = height * (right - left - 1);
res = Math.max(area, res);
}
return res;
}
public static void main(String[] args) {
int[][] arr = {{1,0,1,1},{1,1,1,1},{1,1,1,0}};
System.out.println(maxAreaMatrix(arr));
}
}
注意: maxAreaRectangle
求最大矩形面積的過程比較特別,對於沒有遍歷到的矩形,暫時不將其考慮進最大拼接矩形的範圍中。比如說矩形2,2,4,6,2
,當遍歷到i=1時,棧頂元素爲1,由於棧頂元素所代表的值爲2,與arr[2]相等,所以需要彈出。當前只遍歷過一個矩形,所以能形成的最大矩形也就是棧頂元素所代表的那個矩形。