求最大子矩阵的大小 + 栈

题目:给定一个整型矩阵map, 其中的值只有0 和 1 两种, 求其中全是1 的所有矩形区域中, 最大的矩形区域为1的数量。

例如:

1 0 1 1

1 1 1 1 

1 1 1 0

其中,最大的矩形区域有6个1,所以返回6 。


分析:

如果矩阵的大小为 O(N * M) , 如何达到时间复杂度为O( N* M) ? 

1 : 矩阵的行数为 N, 以每一行做切割, 统计以当前行作为低的情况下,每个位置往上1的数量,使用高度数组height来表示。

2:对每一次切割, 都利用更新后的height数组来求出以每一行为底的情况下,最大的矩形是什么,那么这么多次切割中,最大的那个矩形就是我们要的。

对于步骤2,如何快速的实现,时间复杂度为O(M) ? 用栈

对于height数组 , 将其理解为一个直方图,步骤2的实质是在一个大的直方图中求最大矩形的面积。对每一根柱子,找到柱子左边第一个比它小的柱子的位置和柱子右边第一个比它大的柱子的位置 , 就可以轻松地求出以该根柱子扩展出去的最大矩形。

栈的使用:

从左到右遍历数组height, 遍历到i元素:

关键逻辑:

1: 只有当前i位置的值大于栈顶位置所代表的值,元素i压入

2:如果当前i位置的值小于或等于当前栈顶位置所代表的值,则把栈中存的位置不断弹出,直到某一个栈顶所代表的值小于当前位置的值,再把i压入

 在弹出时:

1: 假设当前弹出的栈顶位置记为j,弹出栈顶之后,新的栈顶记为k,

对于柱子j,向右最远能扩到 i- 1 , 如果所有元素压入栈后,栈不为空,则向右最远扩到height.length,

对于柱子j,向左最远能扩到k + 1 , 如果此时栈为空,向左最远扩到0,说明略


代码如下:

import java.util.*;
public class Main {
	public static void main(String [] args){
		Main test = new Main() ;
		int [][] map = {{1, 0, 1, 1 }, {1, 1, 1 ,1} ,{1, 1, 1, 0} };
//		for(int [] a : map){
//			for(int b : a){
//				System.out.print(" " + b );
//			}
//			System.out.println() ;
//		}
		int maxArea = test.maxRecSize(map) ;
		System.out.println("maxArea = " + maxArea);
	}
	public int maxRecSize(int [][] map){
		if(map == null || map.length == 0 || map[0].length == 0){
			return 0 ;
		}
		int maxArea = 0;
		int [] height = new int[map[0].length] ;
		for(int i = 0 ; i< map.length ; i++){
			for(int j = 0 ; j < map[0].length ; j++){
				height[j] = map[i][j] == 0 ? 0 : height[j] + 1 ;
			}
			maxArea = Math.max(maxArea, maxRecFromBottom(height)) ;
		}
		return maxArea ;
	}
	public int maxRecFromBottom(int [] height){
		if(height == null || height.length == 0){
			return 0 ;
		}
		int maxArea = 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() ;  // 出栈,向后扩展到 i - 1
				int k = stack.isEmpty() ? -1 : stack.peek() ; // 向左扩展到 k + 1
				int curArea = (i - k  - 1) * height[j] ; // 面积
				maxArea = Math.max(maxArea, curArea) ;
			}
			stack.push(i) ; //每个位置进栈一次
		}
		while(!stack.empty()){ //剩余的非空栈的特殊处理
			int j = stack.pop() ;
			int k = stack.empty() ? -1 : stack.peek() ;// 如果栈空,向左扩展到0 , 否则向左扩展到栈顶的位置+1
			int curArea = (height.length - k - 1) * height[j] ;//向右能扩展到height.length -1 
			maxArea = Math.max(maxArea, curArea) ;
		}
		return maxArea ;
	}
}



  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值