截止到目前我已经写了 600多道算法题,其中部分已经整理成了pdf文档,目前总共有1000多页(并且还会不断的增加),大家可以免费下载
下载链接:https://pan.baidu.com/s/1hjwK0ZeRxYGB8lIkbKuQgQ
提取码:6666
问题分析
题中说了矩阵中的数字只有0
和1
,我们可以申请一个二维数组temp,其中temp[i][j]
表示坐标(i,j)
左边连续1的个数。很明显如果坐标(i,j)
位置是0
,那么temp[i][j]=0
。如果坐标(i,j)
位置是1
,那么temp[i][j]=temp[i][j-1]+1
,这里要注意边界条件的判断。我们以示例2
为例画个图来看下
假设是一维数组,也就是说矩形的高度是1
,我们可以发现,以当前位置为矩形的最右边总共有temp[i][j]
个子矩形,如下图所示。
如果矩形的高度都是1,我们只需要把二维数组temp中的所有值相加即可,但实际上矩形的高度不可能都是1
。如果当前位置左边连续1
的个数不为0
,我们计算完高度为1
的子矩形之后还要计算高度为2,3……
的子矩形,直到不能构成矩形为止,如下图所示。
上面高度为2
的子矩形个数是怎么确定的呢,其实就是以红色位置往上找左边连续1
的最小值,由图中我们可以看到3
比4
小,所以高度为2
的子矩形宽度最大是3
,也就是高度为2
的子矩形个数是3
。
那么什么情况下构不成矩形呢,就是遇到了0
,如下图所示,即使0
上面还有更大的数字,他也不可能和下面红色位置构成矩形。
搞懂了上面的分析过程,代码就很容易写了。来看下代码
public int numSubmat(int[][] mat) {
int m = mat.length;//矩阵的宽
int n = mat[0].length;//矩阵的高
//数组temp[i][j]表示坐标(i,j)左边连续1的个数
int[][] temp = new int[m][n];
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
if (j == 0) {
//如果是第一列,连续1的个数就是当前数字
temp[i][j] = mat[i][j];
} else if (mat[i][j] == 1) {
//如果不是第1列,并且当前位置是1,
// 那么左边连续1的个数就
//是temp[i][j - 1] + 1
temp[i][j] = temp[i][j - 1] + 1;
} else {
//当前位置是0,连续1的个数就是0
temp[i][j] = 0;
}
}
}
//记录矩形的个数
int res = 0;
for (int i = 0; i < m; i++) {
for (int j = 0; j < n; j++) {
//temp[i][j]表示的是左边连续1的个数,我们
//可以把它看做是矩形的宽度
int width = temp[i][j];
//往上找矩形的高度,直到遇到0为止
for (int k = i; k >= 0; k--) {
//最小的宽度作为矩形的宽度
width = Math.min(width, temp[k][j]);
res += width;
//如果宽度为0,就不能往上查找
if (width == 0)
break;
}
}
}
return res;
}
时间复杂度:O(n*m^2)
,最上面计算temp
值的时间复杂度是O(m*n)
,下面统计矩形个数的时间复杂度是O(m*n*m)
,最差情况下矩阵全是1
,k
每次都会从i
到0
。所以整体时间复杂度是O(n*m^2)
。
空间复杂度:O(m*n)
,使用一个m*n
的二维数组