一、原题:

给你一个二维矩阵,权值为False和True,找到一个最大的矩形,使得里面的值全部为True,输出它的面积。

样例:

给你一个矩阵如下:

[[1, 1, 0, 0, 1],[0, 1, 0, 0, 1],[0, 0, 1, 1, 1],[0, 0, 1, 1, 1],[0, 0, 0, 0, 1]
]

输出6

二、解题思路:

1、首先,第一种解题方法,也就是最简单最容易想到的方法,就是暴力遍历二维数组中的每一个元素,然后求出该元素所在区域的最大矩形的面积,但是这种方法的时间复杂度太高,不建议这样子做。

2、接下来,介绍另外一种方法: Histogram法

根据下图,可以看出本题可以转化为Largest Rectangle in Histogram来做。

看到这里,可能有些人会不明白什么是Histogram法,那么,在贴出本题的解决代码之前,我们先介绍一下什么是Histogram法。

三、Histogram法:

本部分参考博客:https://blog.csdn.net/hopeztm/article/details/7868581

1、题目大意:

给出一个柱形统计图(histogram), 它的每个项目的宽度是1, 高度和具体问题有关。 现在编程求出在这个柱形图中的最大面积的长方形。

例如:

7 2 1 4 5 1 3 3

7表示柱形图有7个数据,分别是 2 1 4 5 1 3 3, 对应的柱形图如下,最后求出来的面积最大的图如右图所示。

2、分析:

如果采用枚举的方式,如果当前我们枚举项是 i = 0, 即 height = 2时, 我们用另外两个变量 j 和k 向左和向右两个方向搜素,分别找到第一个小于当前height的下标,这样我们就可以算出用 i 项作为高度长方形的面积了。

我们假设 -1位置,和最右高度都是无穷小。

例如:

i = 0, j = -1, k = 1, 最后的面积是 (k - j - 1) * height = 2

i = 1, j = -1, k = 7, 最后面积是( k - j - 1) * height = 7;

...

i = 3, j = 2, k = 5 面积是 ( k - j - 1) * height = 8

枚举出所有的长方形的同时,然后得到最后的面积。

不过这样的程序的时间复杂度是 O(n^2)

3、我们如何能仅仅做一次,就求出这个面积呢?

观察:

当我们扫扫描到第一个高度 H1 = 2的时候,我可以标记它的起始位置1, 因为我们还不知道它将向右扩展到什么地方,所以继续扫面。

当遇到第二项 H2 = 1, 因为这项比之前的小,我们知道,用H1做高度的长方形结束了,算出它的面积。

同时这个时候,我们多了一个高度H2,用它做长方形高度的长方形起始位置应该是在哪里呢? 因为H1的高度比H2要高,所以这个起始位置自然是H1所在的位置。

为了模拟上面的过程,我们引入单调栈,并使用Node对象用于保存的每一项数据:

//节点
class Node{//矩形高度Integer height;//矩形坐标Integer startIndex;Node(Integer height,Integer startIndex){this.height=height;this.startIndex=startIndex;}public Integer getHeight() {return height;}public void setHeight(Integer height) {this.height = height;}public Integer getStartIndex() {return startIndex;}public void setStartIndex(Integer startIndex) {this.startIndex = startIndex;}
}

然后我们按照高度来组织成单调栈。我们来看一下它是如何工作的。

为了不用考虑堆栈为空的情况,我们用插入栈底 一个高度(0, 0)的项。

数据:2 1 4 5 1 3 3

这样初始化:

(0,0)

i=1:

当扫描到(2,1)时候,因为高度2 大于栈顶,插入

(0,0),(2,1)

i=2:

当扫描到1的时候,因为1小于栈顶高度2, 我们认为栈顶的那个高度应不能再向右扩展了,所以我们将它弹出,这个时候扫描到   i = 2;

高度是 (i - (H1.startIndex)) * H1.height = 2;

我们得到一个面积是2的长方形。

同时我们发现高度是1的当前高度,可以扩展到 H1所在的下标,所以我们插入( 1, 1) 堆栈变成

(0, 0), (1, 1) 因为(2, 1)已经不能向右伸展了,已经被弹出了。

i=3 :

(0,0),(1,1),(4,3)

i=4 :

(0, 0), (1, 1), (4, 3), (5, 4)

i = 5

这个时候当前高度小于栈顶高度,我们认为栈顶已经不能向右扩展,所以弹出,并且获得面积 ( i  - H5.startindex) * H5.height = (5 - 4 ) * 5 = 5

弹出这个元素后,其实(4, 3)的高度也要比 1 大,所以把这个也弹出来,同样方式获得面积 8.

最后我们的堆栈是

(0,0),(1,1)

i  = 6

(0, 0), (1, 1), ( 3, 6)

i = 7

(0, 0), (1, 1), (3, 6)

i = 8

最后一步是有点特殊的,因为我们必须要把所有的元素都弹出来,因为栈里面的高度,都坚持到了最后,我们要把这些高度组成的长方形拿出来检测。

我们可以假设扫面到8的时候,高度是0,(最小值)

弹出(3,6)获得面积 (8 - 6 ) * 3 = 6

弹出(1, 1)获得面积(8 - 1) * 1 = 7

最后的面积是8。

4、代码如下:

public class Test3 {public static void main(String[] args) {Integer[] array=new Integer[]{2,1,4,5,1,3,3};Integer max=countArea(array);System.out.println(max);}//histogram图形法:public static Integer countArea(Integer[] array){Stack<Node> stack = new Stack<Node>();stack.push(new Node(0,0));List<Integer> list=new ArrayList<Integer>();//扫面for(int i=0;i<=array.length;i++){//当将所有元素有扫了一遍之后,需要将栈堆弹空,并计算每一个矩形的面积if(i==array.length){//判断是否弹到第一个元素(0,0),是的话就结束,返回最大面积。while(stack.peek().getStartIndex()!=0){Integer area=(i+1-stack.peek().getStartIndex())*stack.peek().getHeight();list.add(area);stack.pop();}return Collections.max(list);}//新的元素比前一个元素的高度高,则入栈if(array[i]>=stack.peek().getHeight()){stack.push(new Node(array[i],i+1));}else{int index=0;//新的元素比前一个元素的高度高,则计算当前矩形的面积,并出栈while(array[i]<stack.peek().getHeight()){Integer area=(i+1-stack.peek().getStartIndex())*stack.peek().getHeight();list.add(area);index=stack.peek().getStartIndex();stack.pop();}//将新的元素入栈stack.push(new Node(array[i],index));}}return 0;}
}//节点
class Node{//矩形高度Integer height;//矩形坐标Integer startIndex;Node(Integer height,Integer startIndex){this.height=height;this.startIndex=startIndex;}public Integer getHeight() {return height;}public void setHeight(Integer height) {this.height = height;}public Integer getStartIndex() {return startIndex;}public void setStartIndex(Integer startIndex) {this.startIndex = startIndex;}
}

上面代码可以换一种简介点的写法:

public class LargestRectangleArea {public int largestRectangleArea(int[] heights) {if(heights==null || heights.length==0) return 0;Stack<Integer> stack = new Stack<>();int res=0;for(int i=0;i<heights.length;i++){while(!stack.isEmpty() && heights[i]<=heights[stack.peek()]){int j=stack.pop();int k=stack.isEmpty()?-1:stack.peek();int curArea=(i-k-1)*heights[j];res=Math.max(res, curArea);}stack.push(i);}while(!stack.isEmpty()){int i=stack.pop();int k=stack.isEmpty()?-1:stack.peek();int curArea=(heights.length-k-1)*heights[i];res=Math.max(res, curArea);}return res;}
}

四、二维矩阵中的最大面积--Java代码实现:

介绍完histogram方法,我们也可以参照histogram方法解决二维矩阵中的最大面积问题。

1、步骤:

(1)接受控制台输入的参数;

(2)重新构造成直方图类型的矩阵。

例如:

重构前:
1 1 0 0 1
0 1 0 0 1
0 0 1 1 1
0 0 1 1 1
0 0 0 0 1
重构后:
1 2 0 0 5
0 1 0 0 4
0 0 2 2 3
0 0 1 1 2
0 0 0 0 1

其中,每一行的数字,代表以当前行为底,直方图的高度。

(3)遍历每一行的,算出当前二维数组的最大矩形面积:

2、完整代码:

package com.zwp.test1;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Scanner;
import java.util.Stack;
/**
* @version 创建时间:2018年8月24日 上午9:07:44
* 类说明 :
* 给你一个二维矩阵,权值为False和True,找到一个最大的矩形,使得里面的值全部为True,输出它的面积
*
给你一个矩阵如下
[[1, 1, 0, 0, 1],[0, 1, 0, 0, 1],[0, 0, 1, 1, 1],[0, 0, 1, 1, 1],[0, 0, 0, 0, 1]
]
输出6
*/
public class Test3 {public static void main(String[] args) {Integer[][] array = build();Integer[][] newArray=rebuild(array);List<Integer> area=new ArrayList<Integer>();for(int i=0;i<newArray.length;i++){Integer temp=countArea(newArray[i]);area.add(temp);}System.out.println(Collections.max(area));}//接收控制台输入的二维数组public static Integer[][] build(){Scanner in =new Scanner(System.in);int row=in.nextInt();int column=in.nextInt();Integer[][] array=new Integer[row][column];for(int i=0;i<row;i++){for(int j=0;j<column;j++){array[i][j]=in.nextInt();}}return array;}//重构二维数组,变成histogram类型。public static Integer[][] rebuild(Integer[][] array){Integer[][] newArray= new Integer[array.length][array[0].length];for(int i=0;i<array.length;i++){for(int j=0;j<array[0].length;j++){int height=0;for(int k=i;k<array.length;k++){if(array[k][j]==1){height+=1;}else{break;}}newArray[i][j]=height;}}return newArray;}//histogram图形法:public static Integer countArea(Integer[] array){Stack<Node> stack = new Stack<Node>();stack.push(new Node(0,0));List<Integer> list=new ArrayList<Integer>();//扫面for(int i=0;i<=array.length;i++){//当将所有元素有扫了一遍之后,需要将栈堆弹空,并计算每一个矩形的面积if(i==array.length){//判断是否弹到第一个元素(0,0),是的话就结束,返回最大面积。while(stack.peek().getStartIndex()!=0){Integer area=(i+1-stack.peek().getStartIndex())*stack.peek().getHeight();list.add(area);stack.pop();}return Collections.max(list);}//新的元素比前一个元素的高度高,则入栈if(array[i]>=stack.peek().getHeight()){stack.push(new Node(array[i],i+1));}else{int index=0;//新的元素比前一个元素的高度高,则计算当前矩形的面积,并出栈while(array[i]<stack.peek().getHeight()){Integer area=(i+1-stack.peek().getStartIndex())*stack.peek().getHeight();list.add(area);index=stack.peek().getStartIndex();stack.pop();}//将新的元素入栈stack.push(new Node(array[i],index));}}return 0;}
}//节点
class Node{//矩形高度Integer height;//矩形坐标Integer startIndex;Node(Integer height,Integer startIndex){this.height=height;this.startIndex=startIndex;}public Integer getHeight() {return height;}public void setHeight(Integer height) {this.height = height;}public Integer getStartIndex() {return startIndex;}public void setStartIndex(Integer startIndex) {this.startIndex = startIndex;}
}

二维矩阵中的最大矩形面积--java实现相关推荐

  1. 提取二维矩阵中分块后指定的块

    对一个二维矩阵I(N×N)进行分块(块大小为n×n),并提取其中第ii块中的元素 % 对二维矩阵I进行[n n]分块,取其中第ii块中的元素 function x = getBlock(I, n, i ...

  2. matlab找出二维矩阵中最大值的位置或者最小值的位置

    matlab寻找最大值或者最小值是通过max和min命令 对应二维矩阵寻找最大元素就是max(max(A)),注意二维矩阵要写两个max 找对应位置用find函数 举个例子: >> A=[ ...

  3. 01二维矩阵中最大全为1的正方形maxSquare——经典DP问题(二维)

    在一个二维01矩阵中找到全为1的最大正方形 1 0 1 0 0 1 0 1 1 1 1 1 1 1 1 1 0 0 1 0 以矩阵中每一个点作为正方形右下角点来处理,而以该点为右下角点的最大边长最多比 ...

  4. python库numpy使用技巧(二)——随机抽取二维矩阵中多行或多列

    使用库numpy 创建一个二维数组 import numpy as nparray = np.arange(24).reshape((4,6))""" array([[ ...

  5. MATLAB 如何在二维矩阵中快速找到最大值的位置

    网上找了找方法都比较冗长,适合于复杂的矩阵,但对于简单的二位矩阵找最大值有没有简易方法? 直接贴代码: [m,j]=max(max(ave_pre)):%%得到的是整个矩阵最大值的值和纵坐标 [m,i ...

  6. java 删除二维数组中的null_避免在Java中检查Null语句

    1.概述 通常,在Java代码中处理null变量.引用和集合很棘手.它们不仅难以识别,而且处理起来也很复杂.事实上,在编译时无法识别处理null的任何错误,会导致运行时NullPointerExcep ...

  7. 二维矩阵中行列元素互换(例题

    #include <stdio.h>int main() {int a[2][3]={{1,2,3},{4,5,6}};int b[3][2],i,j;printf("array ...

  8. Java黑皮书课后题第8章:*8.27(列排序)用下面的方法实现一个二维数组中的列排序。返回新数组,且原数组保持不变。编写一个测试程序,提示用户输入一个3*3的double型矩阵,显示一个排好的矩阵

    *8.27(列排序)用下面的方法实现一个二维数组中的列排序.返回新数组,且原数组保持不变.编写一个测试程序,提示用户输入一个3*3的double型矩阵,显示一个排好的矩阵 题目 题目描述与运行示例 破 ...

  9. Java黑皮书课后题第8章:*8.26(行排序)用下面的方法实现一个二维数组中的行排序。返回新数组,且原数组保持不变。编写一个测试程序,提示用户输入一个3*3的double型矩阵,显示一个排好的矩阵

    *8.26(行排序)用下面的方法实现一个二维数组中的行排序.返回新数组,且原数组保持不变.编写一个测试程序,提示用户输入一个3*3的double型矩阵,显示一个排好的矩阵 题目 题目描述与运行示例 破 ...

最新文章

  1. 基于R的信用评分卡模型解析
  2. 使用URI设计松散耦合的Metro应用程序
  3. 为什么数据库有时候不能定位阻塞(Blocker)源头的SQL语句
  4. xcode5切换IOS7,IOS6,IOS5模拟器
  5. Java-20180419
  6. android 开源框架
  7. java当月日历_java程序——输出当月日历表
  8. 使用PowerShell 导出Exchange中的用户中用户信息到Office 365
  9. win10用什么软件测试xone手柄,win10系统连接xbox手柄的操作方法
  10. python实时语音转写_语音识别 - 实时语音转写 - 《科大讯飞REST_API开发指南》 - 书栈网 · BookStack...
  11. 无线路由器连接无线路由器
  12. 最新《Linux系统优化+Linux综合架构课程》
  13. 阿里投资分众,大战在即的电梯广告市场却有本糊涂账?
  14. android手机图片质量,安卓手机对比iOS照片在微信分享时,竟大幅深压缩画质?...
  15. 生活中的统计概率思维
  16. 补货中估计提前期不确定的需求分布公式推导
  17. 论文笔记:基于深度学习的遥感影像变化检测综述
  18. MTK平台camera小结(一)
  19. 职业教育增长,正在“电商化”
  20. 计算机主机型号查询,如何查询电脑硬件的型号

热门文章

  1. 六十一、Vue中父子组件传值和组件参数校验
  2. 二十六、深入HashCode与equals的区别(上篇)
  3. ICLR 2019 | 基于复杂空间关系旋转的知识表示方法
  4. Floyd算法的动态规划本质
  5. python中使用什么表示代码块、不需要使用大括号_Python3学习笔记02-基础语法
  6. 【在线记事本】一个程序员的随笔(与技术无关)
  7. oracle修改10到20,Oracle 10.2.0.5 RMAN迁移并升级11.2.0.4一例
  8. 【docker常用镜像命令总结,持续更新中... 建议收藏!】
  9. web.py——运行错误【AttributeError: ‘StaticApp‘ object has no attribute ‘directory‘】
  10. Windows10 家庭版——开启Hyper-V虚拟机