1. 程式人生 > >資料結構與演算法——求最大子矩陣問題

資料結構與演算法——求最大子矩陣問題

原題:給定一個整形矩陣map。其中的值只有0和1兩種,求其中全是1的所有矩形區域中,最大的矩形區域為1的數量。

變形題1:

有一個直方圖,用一個整數陣列表示,其中每列的寬度為1,求所給直方圖包含的最大矩形面積。比如,對於直方圖[2,7,9,4],它所包含的最大矩形的面積為14(即[7,9]包涵的7x2的矩形)。

給定一個直方圖A及它的總寬度n,請返回最大矩形面積。保證直方圖寬度小於等於500。保證結果在int範圍內。

      本題應用經典的棧模型來解決。首先本題的解題思路為,建立一個直方圖,從每一個直方圖的兩邊找到比他小的位置,兩個座標的差值就是當前的最大面積。這種模型恰恰與棧的資料結構對應。首先建立一個棧,遍歷每一個矩形的長度,如果比前一個值大,那麼就把當前矩形值的索引值壓入棧中,如果小於等於前一個值,開始結算,當前值記為a,

棧中的值為b,b的下一個位置為c,那麼a-c 就是b矩形的最大面積。通過程式碼再來深入理解。

public class ZuidaJuzhen {
	
	/*
	 * 思路:第一個for while迴圈。不斷的向棧中新增座標,如果當前座標所代表的值大於棧頂則新增成功,小於等於新增失敗。
	 * 這樣可以保證棧頂元素記錄的是遍歷到當前座標位置的值是不斷增大的。這是計算之前的所有座標到最大座標這段距離的面積。
	 * 最後一個while迴圈的時候,肯定棧中還剩一部分元素,這是採用同樣的辦法,計算之前的所有座標到最大座標這段距離的面積,
	 * 只不過最大座標變成了陣列長度。
	 * 
	*/
	public static int maxfrombottom(int[] height){
		int max=0;
		Stack<Integer> stack = new Stack<Integer>();
		for(int i=0;i<height.length;i++){
			while(!stack.empty()&&height[i]<=height[stack.peek()]){
				int j = stack.pop();
				// 此處當stack.empty是 k=-1; 很重要的地方。兩個原因。1.當結算到棧底元素時,可以保證結算區域的正確性。2.就是當遇到兩個相同的元素的時候也結算。遇到兩個相同元素也結算
				// 這時相同元素的第一個的結算可能是不對的,舉例: 3,4,5,3,4,1 這時第一個3 的結算區域不對(可以結算到4,但是第二個3也結算,只結算到3,所以少了一個),但是不對也沒關係,因為當
				// 1結算時會結算3 這時候3時棧底元素,所以k=-1,令第一個三錯過的區域,這時候補上了,不會漏。
				int k = stack.empty()?-1:stack.peek();
				int cur = (i-k-1)*height[j];
				max = Math.max(max, cur);
			}
			stack.push(i);
		}
		// 這個while迴圈用來收底,因為最後一個元素進入之後是需要計算的。 
		while(!stack.empty()){
			int j = stack.pop();
			int k = stack.empty()?-1:stack.peek();
			int cur = (height.length-k-1)*height[j];
			max = Math.max(max, cur);
		} 
		return max;
	}
	
	public static void main(String[] args) {
		
		int[] height = {3,5,6,1};
		System.out.println(maxfrombottom(height));
		
	}
}

這道題的思路可以解決一類問題。比如計算一個矩陣中,只有1和0組成,求出每一個都為1所組成的矩陣中,面積最大的一個,也能轉換成這種模型來計算。

核心思路就是,棧這種資料結構用來解決 從兩邊找第一個比自己小的數。