最大黑方阵(面试题 17.23)O(n³) 预处理枚举解法全解析|doocs/leetcode 题解
示例工程教程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载导读本篇基于 doocs/leetcode 仓库中《程序员面试金典第 6 版》系列的 17.23.Max Black Square 题解深入剖析「最大黑方阵」这一经典矩阵问题的完整解法先通过自右下角的逆序递推预处理出每个格子向下、向右连续黑色像素的长度down与right再由大到小枚举边长、由小到大枚举左上角以 O(1) 的四角判定找出最大黑边方阵。读完本文你将掌握这一套「预处理 枚举」的通用模板可迁移到 1139 题「最大的以 1 为边界的正方形」并拿到 Python3、Java、C、Go、TypeScript、Swift 六种语言的完整可运行实现。![最大黑方阵问题示意图](https://raw.gitcode.com/doocs/leetcode/raw/9e4f89e3ad61b8c7cdd69e3416a405f70334b604/lcci/17.23.Max Black Square/images/max_black_square.png?utm_sourcegitcode_repo_files)一、题目背景与语义该题出自《程序员面试金典第 6 版》第 17 章是 LCCI 系列中典型的「矩阵 动态规划」问题在仓库 lcci/README.md 中被标注为难度「中等」、标签「动态规划」。题目描述给定一个方阵其中每个单元像素非黑即白。0 代表黑色像素1 代表白色像素。要求设计算法找出4 条边皆为黑色像素的最大子方阵返回一个数组[r, c, size]r、c分别代表子方阵左上角的行号、列号size是子方阵的边长若有多个满足条件的子方阵返回r最小的若r相同返回c最小的若不存在满足条件的子方阵返回空数组。注意「边皆为黑」与「整个区域皆为黑」是两回事本题只要求四条边框线上的像素全为黑色方阵内部可以有白色像素。示例 1输入: [ [1,0,1], [0,0,1], [0,0,1] ] 输出: [1,0,2] 解释: 输入中 0 代表黑色1 代表白色标粗的元素即为满足条件的最大子方阵以左上角(1, 0)、边长2的子方阵为例其四条边为上边(1,0)-(1,1)、下边(2,0)-(2,1)、左边(1,0)-(2,0)、右边(1,1)-(2,1)全部为黑色因此答案为[1,0,2]。示例 2输入: [ [0,1,1], [1,0,1], [1,1,0] ] 输出: [0,0,1]主对角线上的三个黑点互不相连只有单个黑色像素(0,0)能构成边长为 1 的黑边方阵故返回[0,0,1]。提示matrix.length matrix[0].length 200即方阵最大边长 200数据规模决定了我们需要一个优于暴力枚举的算法。二、朴素思路与优化动机先看最直观的暴力做法枚举所有可能的左上角(i, j)与边长k再逐边扫描四条边框上的像素是否全黑。左上角有 O(n²) 种选择边长有 O(n) 种选择每条边最多需要检查 O(n) 个像素。因此朴素枚举的复杂度高达 O(n⁴)在 n 200 的规模下显然不可接受。优化思路文档中的核心思考如果预先知道每个格子向下、向右连续黑色像素的长度那么判定「边长为 k 的正方形四边全黑」就不再需要逐像素扫描——四条边分别可以压缩为四个角的四段连续信息从而把单次判定降到 O(1)整体复杂度降为 O(n³)。三、预处理从右下角逆序递推 down 与 right定义两个与输入同尺寸的二维数组down[i][j]从(i, j)出发向下连续黑色像素值为 0的个数right[i][j]从(i, j)出发向右连续黑色像素值为 0的个数。递推公式如下原文公式完整保留$$ down[i][j] \begin{cases} down[i 1][j] 1, matrix[i][j] 0 \textit{ 且 } i 1 n \ 1, matrix[i][j] 0 \textit{ 且 } i 1 n \ 0, matrix[i][j] 1 \end{cases} $$$$ right[i][j] \begin{cases} right[i][j 1] 1, matrix[i][j] 0 \textit{ 且 } j 1 n \ 1, matrix[i][j] 0 \textit{ 且 } j 1 n \ 0, matrix[i][j] 1 \end{cases} $$关键点遍历顺序必须是从右下角向左上角逆序。因为down[i][j]依赖下方邻居down[i 1][j]right[i][j]依赖右方邻居right[i][j 1]只有逆序枚举i和j才能在计算当前格时保证其依赖项已经就绪。以 Python 实现为例仓库 Solution.pyclass Solution: def findSquare(self, matrix: List[List[int]]) - List[int]: n len(matrix) down [[0] * n for _ in range(n)] right [[0] * n for _ in range(n)] for i in range(n - 1, -1, -1): for j in range(n - 1, -1, -1): if matrix[i][j] 0: down[i][j] down[i 1][j] 1 if i 1 n else 1 right[i][j] right[i][j 1] 1 if j 1 n else 13.1 手工推演一个 3×3 小例子以示例 1 的矩阵为例[1,0,1] [0,0,1] [0,0,1]右下角(2, 2)是白格down[2][2] right[2][2] 0(2, 1)是黑格且处于末行、末列down[2][1] 1、right[2][1] 1(2, 0)同理得down 1、right 1。往上到(1, 1)黑格down[1][1] down[2][1] 1 2、right[1][1] right[1][2] 1 1。依此类推最终down[1][0] 2、right[1][0] 2这正是后续判定边长 2 的黑边方阵所需的「从左上角 (1,0) 向下向右至少 2 格连续黑」的信息。四、枚举判定从大到小找边长第一次命中即答案预处理完成后进入枚举阶段。策略是边长 k 从大到小枚举n到1保证第一次找到的就是最大边长找到后直接返回避免继续检查更小的正方形左上角 (i, j) 从小到大枚举行优先天然满足题目「r 最小、其次 c 最小」的返回要求——注意当k固定时先命中的左上角就是字典序最小的那个。左上角(i, j)的可取值范围0 i n - k、0 j n - k保证子方阵不越界。对于一个候选子方阵左上角(i, j)边长k其四条边全黑的充要条件是四个角上的连续黑段长度都不小于 k左上角(i, j)向下连续黑长度down[i][j] k覆盖左边框左上角(i, j)向右连续黑长度right[i][j] k覆盖上边框左下角(i k - 1, j)向右连续黑长度right[i k - 1][j] k覆盖下边框右上角(i, j k - 1)向下连续黑长度down[i][j k - 1] k覆盖右边框。即文档中的完整判定条件down[i][j] k 且 right[i][j] k 且 right[i k - 1][j] k 且 down[i][j k - 1] k满足则直接返回[i, j, k]全部枚举完仍无命中返回空数组。为什么四个条件充分因为正方形四条边上的像素数量都是 k 个左边框的 k 个像素恰好是(i, j)向下的 k 格右边框的 k 个像素恰好是(i, j k - 1)向下的 k 格上边框与下边框同理。只要相应起点处的连续黑段长度 ≥ k该条边上的 k 个像素必然全部为黑四条边同时满足即构成完整的黑边正方形且方阵内部是否全黑并不影响本题结论。枚举部分的 Python 实现for k in range(n, 0, -1): for i in range(n - k 1): for j in range(n - k 1): if ( down[i][j] k and right[i][j] k and right[i k - 1][j] k and down[i][j k - 1] k ): return [i, j, k] return []复杂度分析预处理阶段遍历一遍矩阵为 O(n²)枚举阶段边长 k 有 O(n) 种取值每种取值下左上角有 O(n²) 个候选总枚举量为 O(n³)实际为 n、n-1、…、1 的平方和即 O(n³)每次判定 O(1)。因此时间复杂度O(n³)空间复杂度O(n²)down与right两个辅助二维数组。五、多语言实现仓库内的六套同构代码同一套算法在仓库中提供了 6 种语言的完整实现均可直接运行逻辑与 Python 版本完全同构语言仓库文件实现要点JavaSolution.java三元表达式i 1 n ? down[i 1][j] 1 : 1完成递推无结果返回new int[0]CSolution.cpp使用定长二维数组int down[n][n]配合memset清零无结果返回{}GoSolution.go用make初始化切片先置 1 再累加down[i1][j]、right[i][j1]TypeScriptSolution.tsArray.from({ length: n }, () Array(n).fill(0))创建二维数组number[]返回类型SwiftSolution.swiftstride(from:through:by:)实现逆序与降序遍历0...(n - k)闭区间枚举左上角Python3Solution.py全文核心版本见上文以下给出 Java 与 Go 的完整参考实现其余语言见对应文件。JavaSolution.javaclass Solution { public int[] findSquare(int[][] matrix) { int n matrix.length; int[][] down new int[n][n]; int[][] right new int[n][n]; for (int i n - 1; i 0; --i) { for (int j n - 1; j 0; --j) { if (matrix[i][j] 0) { down[i][j] i 1 n ? down[i 1][j] 1 : 1; right[i][j] j 1 n ? right[i][j 1] 1 : 1; } } } for (int k n; k 0; --k) { for (int i 0; i n - k; i) { for (int j 0; j n - k; j) { if (down[i][j] k right[i][j] k right[i k - 1][j] k down[i][j k - 1] k) { return new int[] {i, j, k}; } } } } return new int[0]; } }GoSolution.gofunc findSquare(matrix [][]int) []int { n : len(matrix) down : make([][]int, n) right : make([][]int, n) for i : range down { down[i] make([]int, n) right[i] make([]int, n) } for i : n - 1; i 0; i-- { for j : n - 1; j 0; j-- { if matrix[i][j] 0 { down[i][j], right[i][j] 1, 1 if i1 n { down[i][j] down[i1][j] } if j1 n { right[i][j] right[i][j1] } } } } for k : n; k 0; k-- { for i : 0; i n-k; i { for j : 0; j n-k; j { if down[i][j] k right[i][j] k right[ik-1][j] k down[i][jk-1] k { return []int{i, j, k} } } } } return []int{} }各语言实现之间的细微差异值得注意边界处理Python/Java/C/TypeScript/Swift 采用「先判越界再决定取 1 还是取邻居 1」Go 采用「先置 1再在未越界时累加」的等价写法两者语义一致无答案时的返回Java 返回new int[0]Python/TS/Swift 返回[]C 返回{}Go 返回[]int{}均对应题目要求的「空数组」。六、边界情况与易错点空矩阵 / 零矩阵若matrix为空或全为白1枚举不会命中任何k返回空数组即可全为黑0时第一次枚举k n、左上角(0, 0)即命中返回[0, 0, n]。单点答案当不存在边长 ≥ 2 的黑边方阵时任何单个黑色像素(i, j)都满足down[i][j] 1、right[i][j] 1最终会退化为返回某个[i, j, 1]——这正是示例 2 输出[0,0,1]的原因行优先枚举保证取到r、c最小者。左上角枚举上界i、j必须满足i k - 1 n、j k - 1 n即i n - k、j n - k否则访问right[i k - 1][j]、down[i][j k - 1]会越界。预处理顺序不可颠倒若改为从左上角正序递推down[i][j]会读到尚未计算的down[i 1][j]结果全部为 0判定随之失效。七、变体与迁移1139. 最大的以 1 为边界的正方形本题在仓库中有直接对应的变体题1139. 最大的以 1 为边界的正方形LeetCode 第 147 场周赛 Q3难度中等。两者的解法骨架完全一致对比维度面试题 17.23本文1139 题黑/白定义0 为黑1 为白1 为目标色目标最大黑边方阵的[r, c, size]最大 1 边方阵的面积返回k * k预处理down、right记录连续 0 的长度down、right记录连续 1 的长度判定条件四角连续段 k与本文完全相同复杂度O(n³) / O(n²)O(m × n × min(m, n)) / O(m × n)1139 题允许矩阵非方阵m × n枚举边长时上界取min(m, n)判定命中后直接返回k * k而非坐标数组其余递推公式、逆序遍历顺序、四角判定条件均与本文一致。掌握了本文的「预处理 枚举」模板两道题可以一并拿下。八、小结「最大黑方阵」是一道典型的用空间换时间的矩阵 DP 问题直接扫描四边的暴力做法是 O(n⁴)而通过down、right两张表把「边是否够长」压缩为 O(1) 的四角判定配合「边长从大到小、左上角从小到大」的双重枚举顺序一举将复杂度降为 O(n³)同时天然满足题目对[r, c, size]字典序最小的返回要求。其核心要点可归纳为三点右下角逆序递推保证预处理依赖就绪、四角连续段判定替代逐边扫描、第一次命中即返回保证结果最大且有序。完整多语言实现可参阅仓库 lcci/17.23.Max Black Square 目录下的各Solution.*文件。赞分享示例工程教程【免费下载链接】leetcodeLeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer第 2 版》、《程序员面试金典第 6 版》题解项目地址https://gitcode.com/doocs/leetcode点击查看免费下载相关推荐LeetCode 面试题 17.23 最大黑方阵题解从暴力枚举到记忆化动态规划附 Java/Python 实现LeetCode 面试题 17.23 最大黑方阵题解从暴力枚举到记忆化动态规划附 Java/Python 实现 本题出自《程序员面试金典》面试题 17.2文档教程知识库doocs/leetcode面试题 16.11「跳水板」的分类讨论解法与 O(k) 枚举实现详解doocs/leetcode面试题 16.11「跳水板」的分类讨论解法与 O k 枚举实现详解 本篇围绕 doocs/leetcode 仓库中《程序员面试金典示例工程教程doocs/leetcode 幂集问题全解面试题 08.04 的递归枚举与二进制枚举两种实现doocs/leetcode 幂集问题全解面试题 08.04 的递归枚举与二进制枚举两种实现 本篇技术指南以 doocs/leetcode 仓库中《程序员面试示例工程教程创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

相关新闻

Java BigDecimal精度陷阱与金融级使用指南

Java BigDecimal精度陷阱与金融级使用指南

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 19:54:02 阅读更多 →
Activiti运行时数据表全解析:结构、关系与线上排查实战

Activiti运行时数据表全解析:结构、关系与线上排查实战

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 19:53:01 阅读更多 →
SpringBoot实现丽江文旅平台的高并发与智能推荐

SpringBoot实现丽江文旅平台的高并发与智能推荐

1. 项目概述:丽江文化旅游网站的SpringBoot实现去年接手了一个丽江本地文旅资源整合平台的项目,用SpringBoot全栈开发了一套包含景点展示、线路规划、特产商城的多功能系统。这个项目最有趣的地方在于需要同时处理高并发访问、多维度数据展示和移动端适配…

2026/10/15 19:53:01 阅读更多 →

最新新闻

JMeter 5.6.2压力测试实战:从线程组到报告解读

JMeter 5.6.2压力测试实战:从线程组到报告解读

简介:Apache JMeter 5.6.2 是由 Apache 组织维护的开源纯 Java 压力测试工具,具备跨平台特性,可运行于 Windows、Linux、macOS。其用途在于模拟大量用户并发访问,评估 Web 应用、数据库、FTP 服务器等静态与动态资源的高并发稳定性…

2026/10/15 20:26:01 阅读更多 →
STM32F3开发基座解析:HAL嫁接、ADC同步与OPAMP避坑指南

STM32F3开发基座解析:HAL嫁接、ADC同步与OPAMP避坑指南

简介:本资源是ST官方发布的STM32F3系列微控制器HAL封装库完整固件包(V1.11.0),面向嵌入式初学者、STM32课程学习者及F3平台项目开发者,解决底层驱动适配难、外设配置复杂、代码可移植性差等典型开发痛点。压缩包共2000…

2026/10/15 20:26:01 阅读更多 →
HART主站协议栈实现:嵌入式C语言轻量级开源方案

HART主站协议栈实现:嵌入式C语言轻量级开源方案

简介:本资源是一份面向工业自动化领域嵌入式开发者与协议栈学习者的HART主设备协议栈开源实现,聚焦于HART(Highway Addressable Remote Transducer)通信标准在软件层面的落地,解决智能仪表系统中主控端(如P…

2026/10/15 20:26:00 阅读更多 →
BMN时序动作定位原理:边界匹配网络的矩阵建模与工业落地

BMN时序动作定位原理:边界匹配网络的矩阵建模与工业落地

1. 项目概述:为什么BMN不是又一个“边界检测”模型,而是时序动作定位的范式跃迁BMN(Boundary Matching Network)这个标题乍看平平无奇,像极了CV领域里那些堆叠卷积、加个注意力就起名“XX-Net”的常规操作。但如果你真…

2026/10/15 20:26:00 阅读更多 →
INCA_ProF脚本自动化标定:从执行模型到批量标定与报告生成

INCA_ProF脚本自动化标定:从执行模型到批量标定与报告生成

简介:这份PDF面向汽车电子标定工程师与INCA初学者,系统梳理ProF脚本的编程要点,帮助读者理解如何用脚本定制ECU闪存编程与自动化任务。资源为单文件PDF,压缩包约271KB,内容以图文笔记形式呈现,便于随时查阅…

2026/10/15 20:26:00 阅读更多 →
Advanced Installer 15.2汉化实践:Windows安装包多语言工程化落地

Advanced Installer 15.2汉化实践:Windows安装包多语言工程化落地

简介:Advanced Installer 15.2汉化版是一款面向Windows平台开发者与系统管理员的专业级安装包制作工具,解决传统MSI打包流程复杂、本地化支持弱、跨环境部署兼容性差等痛点,适用于.NET、Java、SQL Server等多技术栈项目的自动化部署场景。资源…

2026/10/15 20:24:58 阅读更多 →

日新闻

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

Docker Compose 容器化部署 ChirpStack LoRaWAN 网络服务器实战

1. 为什么选择容器化方式部署 LoRa 网络服务LoRa 物联网项目落地时,网络服务器是整个链路的核心枢纽。它负责接收网关上报的数据包、完成去重与解密、再把有效载荷分发给上层应用。早期大家习惯把网络服务器直接装在物理机上,依赖一堆系统库和特定版本的…

2026/10/15 0:00:46 阅读更多 →
共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

共享LLM服务下的跨模型自动扩缩容:Kubernetes实战与避坑指南

1. 共享推理服务里,扩缩容到底难在哪做过大模型推理服务部署的人,大概都经历过这样的场景:一个团队共用一套推理集群,A 业务跑的是 7B 的小模型,B 业务跑的是 72B 的大模型,C 业务白天流量高、晚上几乎归零…

2026/10/15 0:00:46 阅读更多 →
Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

Systemd 启动阶段根文件系统只读之谜:fstab 与 remount-fs 服务深度解析

1. 引子:一次内核恐慌引发的追查先从一个真实场景说起。某次我给一台老旧的嵌入式设备折腾系统裁减,为了把启动时间压进两秒以内,我手贱地把一串自认为“没用”的 systemd 服务给 disable 了,其中就包括systemd-remount-fs.servic…

2026/10/15 0:00:46 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/15 16:18:54 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/15 19:41:51 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/15 13:48:15 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 0:14:36 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/14 22:45:47 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/15 9:49:14 阅读更多 →