这道LeetCode 1292题的核心在于如何高效计算二维矩阵中任意矩形区域的和。传统暴力解法需要O(mn)时间复杂度,而二维前缀和技巧可以将查询优化到O(1)时间复杂度。
二维前缀和的核心思想是预处理一个与原矩阵大小相同的辅助矩阵prefix,其中prefix[i][j]表示从矩阵左上角(0,0)到(i-1,j-1)矩形区域内所有元素的和。通过数学推导可以得到递推公式:
code复制prefix[i][j] = matrix[i-1][j-1]
+ prefix[i-1][j]
+ prefix[i][j-1]
- prefix[i-1][j-1]
这个公式的推导过程非常关键:
在实际编码中,我们通常会创建比原矩阵大一圈的prefix矩阵(行列各+1),这样可以统一处理边界情况。这种处理方式有几个优点:
以LeetCode 1292题为例,我们需要先构建前缀和矩阵:
python复制def buildPrefix(matrix):
m, n = len(matrix), len(matrix[0])
prefix = [[0]*(n+1) for _ in range(m+1)]
for i in range(1, m+1):
for j in range(1, n+1):
prefix[i][j] = matrix[i-1][j-1] + prefix[i-1][j] \
+ prefix[i][j-1] - prefix[i-1][j-1]
return prefix
有了前缀和矩阵后,计算任意矩形区域和变得非常简单:
python复制def getSum(prefix, x1, y1, x2, y2):
return prefix[x2][y2] - prefix[x1-1][y2] \
- prefix[x2][y1-1] + prefix[x1-1][y1-1]
这个查询操作的时间复杂度是O(1),无论矩形大小如何。
题目要求找到最大的正方形边长k,使得该正方形内所有元素的和不超过给定阈值。结合前缀和技巧,我们可以:
直接枚举所有k值效率不高,可以采用二分查找优化:
python复制def maxSideLength(matrix, threshold):
m, n = len(matrix), len(matrix[0])
prefix = buildPrefix(matrix)
left, right = 1, min(m, n)
res = 0
while left <= right:
mid = (left + right) // 2
found = False
for i in range(mid, m+1):
for j in range(mid, n+1):
total = getSum(prefix, i-mid+1, j-mid+1, i, j)
if total <= threshold:
found = True
break
if found: break
if found:
res = mid
left = mid + 1
else:
right = mid - 1
return res
这个解法的时间复杂度为O(min(m,n) * m * n),通过二分查找将时间复杂度从O(min(m,n)^2 * m * n)优化到更优。
二维前缀和的思想可以推广到三维甚至更高维度。三维前缀和的递推公式为:
code复制prefix[i][j][k] = val[i-1][j-1][k-1]
+ prefix[i-1][j][k]
+ prefix[i][j-1][k]
+ prefix[i][j][k-1]
- prefix[i-1][j-1][k]
- prefix[i-1][j][k-1]
- prefix[i][j-1][k-1]
+ prefix[i-1][j-1][k-1]
二维前缀和在很多领域都有广泛应用:
常见错误包括:
调试建议:
当矩阵很大时:
提示:在面试中,面试官可能会要求解释算法原理而不仅仅是写出代码,因此要理解每个步骤的数学含义。