分享一个大牛的人工智能教程。零基础!通俗易懂!风趣幽默!希望你也加入到人工智能的队伍中来!请轻击人工智能教程大家好!欢迎来到我的网站! 人工智能被认为是一种拯救世界、终结世界的技术。毋庸置疑,人工智能时代就要来临了,科… 继续阅读 前言https://www.captainai.net/troubleshooter
package live.every.day.ProgrammingDesign.CodingInterviewGuide.ArrayAndMatrix;
/**
* 边界都是1的最大正方形大小
*
* 【题目】
* 给定一个NxN的短阵matrix,在这个矩阵中,只有0和1两种值,返回边框全是1的最大正方形的边长长度。
* 例如:
* 0 1 1 1 1
* 0 1 0 0 1
* 0 1 0 0 1
* 0 1 1 1 1
* 0 1 0 1 1
* 其中,边框全是1的最大正方形的大小为4x4,所以返回4。
*
* 【难度】
* 中等
*
* 【解答】
* 先介绍一个比较容易理解的解法:
* 1.矩阵中一共有NxN个位置。O(N^2)
* 2.对每一个位置都看是否可以成为边长为N-1的正方形左上角。比如,对于(0,0)位置,依次检查是否是边长为5的正方形左上角,然
* 后检查边长为4、3等。O(N)
* 3.如何检查一个位置是否可以成为边长为N的正方形的左上角呢?遍历这个边长为N的正方形边界看是否只由1构成,也就是走过4个边的
* 长度(4N)。O(N)
* 所以普通方法总的时间复杂度为O(N^2)xO(N)xO(N)=O(N^4)。
*
* 本文提供的方法的时间复杂度为O(N^3),基本过程也是如上三个步骤。但是对于步骤3,可以把时间复杂度由O(N)降为O(1)。具体地
* 说,就是能够在O(1)的时间内检查一个位置假设为(i,j),是否可以作为边长为a(1<=a<=N)的边界全是1的正方形左上角。关键是使
* 用预处理技巧,这也是面试经常使用的技巧之一,下面介绍得到预处理矩阵的过程。
*
* 1.预处理过程是根据矩阵matrix得到两个矩阵right和down。right[i][j]的值表示从位置(i,j)出发向右,有多少个连续的1。
* down[i][j]的值表示从位置(i,j)出发向下有多少个连续的1。
*
* 2.right和down矩阵如何计算?
* 1)从矩阵的右下角(n-l,n-1)位置开始计算,如果matrix[n-1][n-1]==1,那么,right[n-1][n-1]=1且down[n-1][n-1]=1,
* 否则都等于0。
* 2)从右下角开始往上计算,即在matrix最后一列上计算,位置就表示为(i,n-1)。对right矩阵来说,最后一列的右边没有内容,所
* 以,如果matrix[i][n-1]==1,则令right[i][n-1]=1,否则为0。对down矩阵来说,如果matrix[i][n-1]==1,因为
* down[i+1][n-1]表示包括位置(i+1,n-1)在内并往下有多少个连续的1,所以,如果位置(i,n-1)是1,那么,令down[i][n-1]
* =down[i+1][n-1]+1;如果matrix[i][n-1]==0,则令down[i][n-1]=0。
* 3)从右下角开始往左计算,即在matrix最后一行上计算,位置可以表示为(n-1,j)。对right矩阵来说,如果matrix[n-1][j]==1,
* 因为right[n-1][j+1]表示包括位置(n-1,j+1)在内右边有多少个连续的1。所以,如果位置(n-1,j)是1,则令right[n-1][j]
* ==right[n-1][j+1]+1;如果matrix[n-1][j]==0,则令right[n-1][j]==0。对down矩阵来说,最后一列的下边没有内容,
* 所以,如果matrix[n-1][j]==1,令down[n-1][j]=1,否则为0。
* 4)计算完步骤1)~步骤3)之后,剩下的位置都是既有右,也有下,假设位置表示为(i,j):
* 如果matrix[i][j]==1,则令right[i][j]=right[i][j+1]+1,down[i][j]=down[i+1][j]+1。
* 如果matrix[i][j]==0,则令right[i][j]==0,down[i][j]==0。
*
* 预处理的具体过程请参看如下代码中的setBorderMap方法。
*
* 得到right和down矩阵后,如何加速检查过程呢?比如现在想检查一个位置,假设为(i,j)。是否可以作为边长为a(1<=a<=N)的边
* 界全为1的正方形左上角。
* 1)位置(i,j)的右边和下边连续为1的数量必须都大于或等于a(right[i][j]>=a&&down[i][j]>=a),否则说明上边界和左边界的
* 1不够。
* 2)位置(i,j)向右跳到位置(i,j+a-1),这个位置是正方形的右上角,那么这个位置的下边连续为1的数量也必须大于或等于
* a(down[i][j+a-1]>=a),否则说明右边界的1不够。
* 3)位置(i,j)向下跳到位置(i+a-l,j),这个位置是正方形的左下角,那么这个位置的右边连续为1的数量也必须大于或等于
* a(right[i+a-1][j]>=a),否则说明下边界的1不够。
* 以上三个条件都满足时,就说明位置(i,j)符合要求,利用right和down矩阵之后,加速的过程很明显,不需要遍历边长上的所有值
* 了,只看4个点即可。
*
* 全部过程请参看如下代码中的getMaxSize方法。
*
* @author Created by LiveEveryDay
*/
public class Border1MaxSquareSize {
public static void setBorderMap(int[][] m, int[][] right, int[][] down) {
int r = m.length;
int c = m[0].length;
if (m[r - 1][c - 1] == 1) {
right[r - 1][c - 1] = 1;
down[r - 1][c - 1] = 1;
}
for (int i = r - 2; i != -1; i--) {
if (m[i][c - 1] == 1) {
right[i][c - 1] = 1;
down[i][c - 1] = down[i + 1][c - 1] + 1;
}
}
for (int i = c - 2; i != -1; i--) {
if (m[r - 1][i] == 1) {
right[r - 1][i] = right[r - 1][i + 1] + 1;
down[r - 1][i] = 1;
}
}
for (int i = r - 2; i != -1; i--) {
for (int j = c - 2; j != -1; j--) {
if (m[i][j] == 1) {
right[i][j] = right[i][j + 1] + 1;
down[i][j] = down[i + 1][j] + 1;
}
}
}
}
public static int getMaxSize(int[][] m) {
int[][] right = new int[m.length][m[0].length];
int[][] down = new int[m.length][m[0].length];
setBorderMap(m, right, down);
for (int size = Math.min(m.length, m[0].length); size != 0; size--) {
if (hasSizeOfBorder(size, right, down)) {
return size;
}
}
return 0;
}
public static boolean hasSizeOfBorder(int size, int[][] right, int[][] down) {
for (int i = 0; i != right.length - size + 1; i++) {
for (int j = 0; j != right[0].length - size + 1; j++) {
if (right[i][j] >= size
&& down[i][j] >= size
&& right[i + size - 1][j] >= size
&& down[i][j + size - 1] >= size) {
return true;
}
}
}
return false;
}
public static void main(String[] args) {
int[][] matrix = new int[][]{
{0, 1, 1, 1, 1},
{0, 1, 0, 0, 1},
{0, 1, 0, 0, 1},
{0, 1, 1, 1, 1},
{0, 1, 0, 1, 1,}};
System.out.printf("The max size is: %d", getMaxSize(matrix));
}
}
// ------ Output ------
/*
The max size is: 4
*/