LeetCode 面试题 17.23. 最大黑方阵
题目描述

题意分析
方阵中
0是黑色、1是白色,寻找四条边全部为黑色的最大正方形,内部颜色不受限制。返回左上角行、列和边长;同边长时行号更小优先,再选列号更小者,没有黑色边框则返回空数组。
解法:预处理连续黑格 + 枚举边长
核心思路
[!blue]
枚举一个左上角与边长后,直接扫描四条边会反复检查相同黑格。可以预先记录连续长度,将整个边框的验证压缩成四次数组查询。
定义
right[row][col]为从当前格向右连续黑格的数量,down[row][col]为向下连续黑格的数量,两者都包含当前格。白格的两个值为零;黑格则在右侧或下侧已有的连续长度上加一。因为依赖右邻与下邻,按行、列都从大到小扫描,使用依赖时它们已经算好。边界没有邻格时只计当前黑格的一。对左上角
(row, col)、边长size,右边界为rightCol = col + size - 1,下边界为bottom = row + size - 1。上边与左边分别要求right[row][col] >= size、down[row][col] >= size;下边从(bottom, col)向右检查,右边从(row, rightCol)向下检查,也都要求连续长度至少为size。四个条件正好覆盖四条边:每个计数足够长,意味着该条边没有白格;四条都通过,整个边框就合法。大于边长的计数同样可以接受,边框之外是否继续为黑色没有影响,内部格子也无需检查。
边长从
n递减枚举,第一次命中的尺寸必然最大。固定尺寸时,左上角先按行升序、再按列升序遍历,所以第一次命中也满足并列时的坐标要求,可以直接返回。一直检查到边长一仍无解,说明没有可用黑格,返回空数组。
解题步骤
- 创建两张连续黑格表,从右下向左上计算向右、向下的连续长度。
- 边长从大到小枚举,遍历能容纳该尺寸的左上角,即
row + size <= n、col + size <= n。- 用左上、左下、右上三个角上的四个计数检查全部边框。
- 首次通过立即返回位置和边长;全部失败则返回空数组。
代码实现
class Solution {
public int[] findSquare(int[][] matrix) {
int n = matrix.length;
int[][] right = new int[n][n];
int[][] down = new int[n][n];
for (int row = n - 1; row >= 0; row--) {
for (int col = n - 1; col >= 0; col--) {
if (matrix[row][col] == 0) {
right[row][col] = 1;
down[row][col] = 1;
if (col + 1 < n) {
right[row][col] += right[row][col + 1];
}
if (row + 1 < n) {
down[row][col] += down[row + 1][col];
}
}
}
}
for (int size = n; size >= 1; size--) {
for (int row = 0; row + size <= n; row++) {
for (int col = 0; col + size <= n; col++) {
int bottom = row + size - 1;
int rightCol = col + size - 1;
if (right[row][col] >= size
&& down[row][col] >= size
&& right[bottom][col] >= size
&& down[row][rightCol] >= size) {
return new int[] {
row,
col,
size,
};
}
}
}
}
return new int[0];
}
}
func findSquare(matrix [][]int) []int {
n := len(matrix)
right := make([][]int, n)
down := make([][]int, n)
for row := 0; row < n; row++ {
right[row] = make([]int, n)
down[row] = make([]int, n)
}
for row := n - 1; row >= 0; row-- {
for col := n - 1; col >= 0; col-- {
if matrix[row][col] == 0 {
right[row][col] = 1
down[row][col] = 1
if col+1 < n {
right[row][col] += right[row][col+1]
}
if row+1 < n {
down[row][col] += down[row+1][col]
}
}
}
}
for size := n; size >= 1; size-- {
for row := 0; row+size <= n; row++ {
for col := 0; col+size <= n; col++ {
bottom := row + size - 1
rightCol := col + size - 1
if right[row][col] >= size &&
down[row][col] >= size &&
right[bottom][col] >= size &&
down[row][rightCol] >= size {
return []int{
row,
col,
size,
}
}
}
}
}
return []int{}
}
复杂度分析
- 时间复杂度:$O(n^3)$。预处理为 $O(n^2)$,最多枚举
n种边长与每种 $O(n^2)$ 个左上角,每个候选只做常数次查询。- 空间复杂度:$O(n^2)$,用于向右和向下的两张计数表。
关键点总结
[!green]
- 只判断边框,四个连续长度已足够,不必检查内部面积。
- 逆序填表与向右、向下的依赖一致,保证连续长度可以直接递推。
- 大尺寸优先、行优先、列次之的遍历顺序,同时处理最大面积和坐标并列规则。
易错点总结
[!yellow]
- 黑色对应零,不能按常见的“一代表目标色”写反条件。
- 下边要从左下角向右查,右边要从右上角向下查,不能重复检查左上角的两条边。
- 右下边界坐标是起点加
size - 1,使用size会越过候选方阵。- 边长从小到大首次返回只会得到较小解;同尺寸先枚举列也可能违反最小行号要求。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 1139. 最大的以 1 为边界的正方形 | 中等 | 同样只要求正方形边框满足条件,原题1为目标色并返回面积,本题0为黑色且返回位置和边长。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!