LeetCode 363题:二维矩阵最大子矩阵和不超过k的优化解法

发布时间:2026/9/17 7:59:33
LeetCode 363题:二维矩阵最大子矩阵和不超过k的优化解法 1. 问题重述与核心挑战LeetCode 363题要求我们解决一个典型的二维矩阵求和问题给定一个m x n的整数矩阵和一个整数k找出矩阵中所有可能的矩形区域使得该区域内数值之和不超过k同时是所有可能情况中的最大值。这个问题的难点在于如何高效地处理二维矩阵中的各种子矩阵组合。对于一个m x n的矩阵理论上存在O(m²n²)个子矩阵直接暴力枚举所有可能性在m和n较大时如100x100会导致计算量达到10^8级别这在常规时间限制内是无法完成的。2. 基础解法二维前缀和2.1 前缀和数组构建二维前缀和是解决这类矩阵区域求和问题的经典方法。我们首先构建一个前缀和数组sum其中sum[i][j]表示从矩阵左上角(0,0)到(i-1,j-1)位置的矩形区域和。构建公式为sum[i][j] sum[i-1][j] sum[i][j-1] - sum[i-1][j-1] matrix[i-1][j-1]这个公式利用了容斥原理避免了重复计算。通过预处理我们可以在O(1)时间内计算任意子矩阵的和。2.2 朴素枚举法有了前缀和数组后最直观的解法是枚举所有可能的子矩阵int maxSum Integer.MIN_VALUE; for (int i1 0; i1 m; i1) { for (int j1 0; j1 n; j1) { for (int i2 i1; i2 m; i2) { for (int j2 j1; j2 n; j2) { int current sum[i21][j21] - sum[i1][j21] - sum[i21][j1] sum[i1][j1]; if (current k) { maxSum Math.max(maxSum, current); } } } } }这种方法虽然直观但时间复杂度为O(m²n²)对于100x100的矩阵来说显然不够高效。3. 优化思路降维与二分查找3.1 从二维到一维的转换为了优化算法我们需要将二维问题转化为一维问题。核心思路是固定矩阵的上下边界将每一列的和看作一个一维数组的元素。具体步骤枚举所有可能的行范围(top, bottom)对于每个行范围计算每一列的和形成一个一维数组在这个一维数组中寻找不超过k的最大子数组和3.2 一维问题的解法对于一维数组我们需要找到子数组和不超过k的最大值。这可以通过前缀和有序集合的方式高效解决计算一维数组的前缀和数组S对于每个j我们需要找到i使得S[j] - S[i] ≤ k这等价于找到i使得S[i] ≥ S[j] - k使用TreeSet维护已遍历的前缀和可以快速查找满足条件的最小S[i]TreeSetInteger set new TreeSet(); set.add(0); // 初始前缀和为0 int currentSum 0; int maxSum Integer.MIN_VALUE; for (int num : nums) { currentSum num; Integer ceil set.ceiling(currentSum - k); if (ceil ! null) { maxSum Math.max(maxSum, currentSum - ceil); } set.add(currentSum); }4. 完整优化算法实现4.1 Java实现class Solution { public int maxSumSubmatrix(int[][] matrix, int k) { int m matrix.length, n matrix[0].length; int[][] sum new int[m 1][n 1]; // 构建前缀和数组 for (int i 1; i m; i) { for (int j 1; j n; j) { sum[i][j] sum[i-1][j] sum[i][j-1] - sum[i-1][j-1] matrix[i-1][j-1]; } } int ans Integer.MIN_VALUE; // 枚举上下边界 for (int top 1; top m; top) { for (int bottom top; bottom m; bottom) { TreeSetInteger treeSet new TreeSet(); treeSet.add(0); // 初始前缀和为0 // 枚举右边界 for (int right 1; right n; right) { // 计算从top到bottom行1到right列的区域和 int area sum[bottom][right] - sum[top-1][right]; // 寻找满足area - x ≤ k的最小x即x ≥ area - k Integer left treeSet.ceiling(area - k); if (left ! null) { ans Math.max(ans, area - left); } treeSet.add(area); } } } return ans; } }4.2 C实现class Solution { public: int maxSumSubmatrix(vectorvectorint matrix, int k) { int m matrix.size(), n matrix[0].size(); vectorvectorint sum(m 1, vectorint(n 1, 0)); // 构建前缀和数组 for (int i 1; i m; i) { for (int j 1; j n; j) { sum[i][j] sum[i-1][j] sum[i][j-1] - sum[i-1][j-1] matrix[i-1][j-1]; } } int ans INT_MIN; // 枚举上下边界 for (int top 1; top m; top) { for (int bottom top; bottom m; bottom) { setint s; s.insert(0); // 初始前缀和为0 // 枚举右边界 for (int right 1; right n; right) { int area sum[bottom][right] - sum[top-1][right]; auto it s.lower_bound(area - k); if (it ! s.end()) { ans max(ans, area - *it); } s.insert(area); } } } return ans; } };5. 进阶优化行列选择策略5.1 行列数差异的处理当矩阵的行数远大于列数或反之时我们可以调整枚举策略以获得更好的性能。具体来说如果行数m 列数n我们改为枚举左右边界然后对每一行进行压缩这样可以减少外层循环的次数从O(m²)降到O(n²)5.2 优化后的实现class Solution { public int maxSumSubmatrix(int[][] matrix, int k) { int m matrix.length, n matrix[0].length; boolean isRowLarger m n; int outerDim isRowLarger ? n : m; int innerDim isRowLarger ? m : n; int ans Integer.MIN_VALUE; // 枚举外层维度行数多时枚举列列数多时枚举行 for (int i 0; i outerDim; i) { int[] compressed new int[innerDim]; for (int j i; j outerDim; j) { // 压缩矩阵 for (int x 0; x innerDim; x) { compressed[x] isRowLarger ? matrix[x][j] : matrix[j][x]; } // 在一维数组上求解 TreeSetInteger set new TreeSet(); set.add(0); int currentSum 0; for (int num : compressed) { currentSum num; Integer ceil set.ceiling(currentSum - k); if (ceil ! null) { ans Math.max(ans, currentSum - ceil); } set.add(currentSum); } } } return ans; } }6. 复杂度分析6.1 时间复杂度基础二维前缀和TreeSet解法预处理O(mn)枚举上下边界O(m²)对每个边界组合处理列O(n log n)总复杂度O(m² n log n)行列优化后的解法外层循环O(min(m,n)²)内层处理O(max(m,n) log max(m,n))总复杂度O(min(m,n)² max(m,n) log max(m,n))6.2 空间复杂度基础解法O(mn)用于存储前缀和数组优化解法O(max(m,n))用于存储压缩数组和TreeSet7. 实际应用与注意事项7.1 边界条件处理在实际编码中需要注意几个关键点TreeSet初始化时要加入0对应空子矩阵的情况矩阵元素可能为负数因此不能使用滑动窗口等基于单调性的优化结果初始值应设为Integer.MIN_VALUE因为可能所有子矩阵和都大于k7.2 性能优化技巧对于特别大的k可以提前检查整个矩阵的和当找到等于k的解时可以立即返回这是最优解在TreeSet操作前可以先检查是否有可能的改进避免不必要的操作7.3 常见错误忘记处理空子矩阵的情况前缀和为0错误计算子矩阵边界导致数组越界在行列优化版本中混淆行和列的处理顺序8. 扩展思考这个问题可以延伸到多个方向如果要求恰好等于k的最大子矩阵和该如何修改如果矩阵可以动态更新如何设计数据结构支持快速查询在分布式环境下如何分割矩阵以并行计算在实际工程中类似的技术可以应用于图像处理中的区域特征提取金融数据分析中的最大收益区域查找地理信息系统中的热点区域分析