栈与队列8——求最大子矩阵的大小

题目

给定一个整形矩阵map,其中的值只有0和1,求其中全是1的所有矩形区域中,最大的矩形区域中1的数量,例如:1 1 1 0,其中最大的矩形区域有3个1,返回3.

再如:

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来存储,比如题目中的3*4的矩阵的举例,以第一行做切割,height={1,0,1,1},以第二行做切割,height={2,1,2,2},以第三行做切割,height={3,2,3,0}
  2. 求解最大矩阵:对每行的height分别去求最大矩阵,最后选出一个最大值即可。
  • 求解最大矩阵有个问题,为了好理解再次举例,如果height={3,2,3,0},则最大矩阵面积为2*3=6
  • 接下来我们把height数组看作直方图,每一个数字代表一个柱子,我们实际就是在求每个柱子能扩展多大的矩形,也就是说,需要找到柱子左右两侧离他最近且比他小的柱子的位置,这样子做就可以通过左右两个边界的差*当前柱子的高=当前柱子可扩展的矩形的面积
  • 需要找到柱子左右两侧离他最近且比他小的柱子的位置,就是我们之前做过的单调栈问题,读者是不是很熟悉,可以看我上一篇文章对于单调栈问题的讲解。
  • 此处建立在读者理解了单调栈结构的基础上,进行进一步对题的分析:

       1>遍历height数组

       2>栈为空或者当前height[i]大于栈顶元素直接入栈

              3>若栈不为空,并且当前height[i]小于栈顶元素 j(假设为j,因为栈里存放的是数组索引值,这里比较的是height[i]<=height[j]),则需要弹出栈顶元素,则对于弹出的元素j来说左边位置离j最近并且小于arr[j]的值为当前栈顶(记为k)对应的值,右边位置则是i;

             4>同理来说,对于当前弹出栈顶j,如果height[j]>height[i],则j所在的柱子向右扩展最多i-1,因为height[i]已经比height[j]小了;如果height[j]==height[i],则j所在的柱子向右扩展至少i-1,因为height[i]只是等于height[j];

扫描二维码关注公众号,回复: 8541330 查看本文章

             5>对于j所在的柱子,向左最远可以扩展到k+1(k为当前栈顶元素,因为height[k]已经比height[j]小了)

             6>综上,j位置能的最大矩形为: [(i-1)-(k+1)+1]*height[j]=(i-k-1)*height[j]
             7> 遍历完数组后,若栈不为空,则对于栈中的所有元素,它们的右边位置最近并且小于他们的值都不存在,我们赋予iheight.length;j位置能的最大矩形公式变为为: [(height.length-1)-(k+1)+1]*height[j]=(height.length-k-1)*height[j]

疑惑解答:对于步骤4>,若height[j]==height[i],则向右至少为i-1,那么求出来的矩形面积不是偏小嘛?其实确实存在,但是不用担心因为我们此刻针对的是j来求解,当i进栈后,再次弹栈时,就会求解出正确的,因为i柱子和j柱子扩展出来的最大矩形是同一个。


源码

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(maxRecFromBottom(height),maxArea);
	}
	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.isEmpty()&&height[i]<=height[stack.peek()]){
			int j=stack.pop();
			int k=stack.isEmpty()?-1:stack.peek();
			int curArea=(i-k-1)*height[j];
			maxArea=Math.max(maxArea,curArea);
		}
		stack.push(i);
	}
	while(!stack.isEmpty()){
		int j=stack.pop();
		int k=stack.isEmpty()?-1:stack.peek();
		int curArea=(height.length-k-1)*height[j];
		maxArea=Math.max(maxArea,curArea);
	}
	return maxArea;
}

此题偏难,建议小伙伴看之前先看一下前面的关于单调栈的文章,有问题欢迎随时交流讨论。

发布了43 篇原创文章 · 获赞 21 · 访问量 4928

猜你喜欢

转载自blog.csdn.net/flying_1314/article/details/103784506