1. 问题背景与理解
这道题目出现在LeetCode周赛中,属于中等偏上难度的矩阵操作类问题。题目给出一个二进制矩阵(只包含0和1),允许我们对矩阵的列进行任意次数的重新排列,目标是找到由全1构成的最大子矩阵的面积。
举个例子,给定矩阵:
code复制1 0 1 1
1 1 1 1
1 1 1 0
通过重新排列列(比如把最后一列移到第二列的位置),可以得到:
code复制1 1 1 0
1 1 1 1
1 1 1 0
此时最大的全1子矩阵是右下角的2×3矩阵,面积为6。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 暴力解法与优化思路
2.1 朴素暴力法的局限性
最直观的想法是尝试所有可能的列排列组合,然后对每种排列计算最大全1子矩阵。对于一个m×n的矩阵,列排列有n!种可能,时间复杂度为O(n!×m×n²),这在n=100时完全不可行。
2.2 关键观察点
通过分析可以发现两个重要性质:
- 列的顺序不影响每行"连续1"的长度计算
- 最大全1子矩阵的高度取决于从某行开始向上的连续全1行
这提示我们可以先对每行进行预处理,计算从该行向上连续的1的个数(类似直方图的高度),然后对每行的这个高度数组进行排序,快速找到可能的最大矩形。
3. 基于直方图的优化解法
3.1 预处理高度数组
首先计算每个位置向上的连续1的个数。对于矩阵matrix[m][n],创建height[m][n]:
python复制height = [[0]*n for _ in range(m)]
for j in range(n):
height[0][j] = matrix[0][j]
for i in range(1,m):
for j in range(n):
height[i][j] = height[i-1][j] + 1 if matrix[i][j] == 1 else 0
3.2 对每行高度排序并计算最大面积
对于每一行的height数组,先排序然后计算可能的最大矩形面积:
python复制max_area = 0
for i in range(m):
sorted_heights = sorted(height[i], reverse=True)
for j in range(n):
area = sorted_heights[j] * (j + 1)
max_area = max(max_area, area)
3.3 复杂度分析
预处理高度数组:O(mn)
每行排序:O(n log n),共m行 → O(mn log n)
总时间复杂度:O(mn log n)
空间复杂度:O(mn)存储高度数组
4. 进一步优化:线性时间处理高度数组
4.1 计数排序的应用
由于高度值不超过m,可以使用计数排序将排序时间降为O(n):
python复制def counting_sort(arr, max_val):
count = [0]*(max_val+1)
for num in arr:
count[num] += 1
sorted_arr = []
for num in range(max_val, 0, -1):
sorted_arr += [num]*count[num]
return sorted_arr
4.2 优化后的实现
python复制max_area = 0
max_height = m # 最大可能高度
for i in range(m):
sorted_heights = counting_sort(height[i], max_height)
for j in range(n):
area = sorted_heights[j] * (j + 1)
max_area = max(max_area, area)
此时时间复杂度降为O(mn),因为计数排序是O(n),执行m次。
5. 完整代码实现
python复制def largestSubmatrix(matrix):
m, n = len(matrix), len(matrix[0])
height = [[0]*n for _ in range(m)]
# 预处理高度数组
for j in range(n):
height[0][j] = matrix[0][j]
for i in range(1, m):
for j in range(n):
height[i][j] = height[i-1][j] + 1 if matrix[i][j] == 1 else 0
max_area = 0
for i in range(m):
# 对当前行的height进行非递增排序
sorted_heights = sorted(height[i], reverse=True)
# 计算可能的最大面积
for j in range(n):
area = sorted_heights[j] * (j + 1)
max_area = max(max_area, area)
return max_area
6. 边界条件与测试案例
6.1 常见测试案例
- 全1矩阵:
code复制输入:[[1,1],[1,1]]
输出:4
- 全0矩阵:
code复制输入:[[0,0],[0,0]]
输出:0
- 单行矩阵:
code复制输入:[[1,0,1,1,1]]
输出:3
- 题目示例:
code复制输入:[[0,0,1],[1,1,1],[1,0,1]]
输出:4
6.2 特殊边界处理
- 空矩阵:题目保证m,n ≥ 1
- 超大矩阵:m,n=1000时,O(mn)解法也能通过
- 极值情况:全0或全1的大矩阵
7. 算法扩展与变种
7.1 允许行重排的情况
如果题目改为允许行和列都重排,解法会更简单:直接统计所有1的个数,最大子矩阵面积就是最大k×l使得k×l ≤ total_ones。
7.2 非二进制矩阵的情况
如果矩阵包含任意数字,求最大子矩阵和,这就是经典的"最大子矩阵和"问题,可以用Kadane算法在O(n³)时间内解决。
7.3 动态规划解法思考
虽然本题有更优解法,但也可以考虑DP思路:
- dp[i][j]表示以(i,j)为右下角的最大全1子矩阵面积
- 状态转移需要考虑左边、上边和左上角的dp值
- 这种方法时间复杂度也是O(mn),但实现起来更复杂
8. 实际应用场景
这种矩阵重排和子矩阵查找技术在多个领域有应用:
- 图像处理:查找二值图像中的最大连通区域
- 数据挖掘:在稀疏矩阵中寻找密集块
- 推荐系统:用户-物品矩阵中的高密度区域可能表示热门组合
- 基因组学:在基因表达矩阵中寻找共同表达模式
9. 解题心得与技巧
- 矩阵列重排类问题,通常要考虑列顺序是否影响计算,有时排序能带来便利
- 预处理技术(如计算高度数组)是矩阵问题的常见优化手段
- 当数值范围有限时,计数排序可以替代比较排序获得线性时间复杂度
- 在LeetCode竞赛中,遇到矩阵问题可以先考虑:
- 预处理行/列前缀和
- 尝试降维(如将二维问题转化为一维)
- 排序是否有助于简化问题
10. 进一步学习建议
-
类似题目推荐:
- LeetCode 85. 最大矩形(类似但不允许重排)
- LeetCode 1504. 统计全1子矩形(统计所有子矩形)
- LeetCode 221. 最大正方形(找最大全1正方形)
-
进阶算法学习:
- 单调栈在矩阵问题中的应用
- Kadane算法及其二维扩展
- 分治法解决矩阵问题
-
实际工程应用:
- OpenCV中的矩阵操作
- NumPy的矩阵优化技巧
- 稀疏矩阵的存储与计算优化
