前缀和是一个数组的某项下标之前(包括此项元素)的所有数组元素的和。
1、一维前缀和
公式:sum[i] = sum[i-1] + a[i]
根据上述表达式我们可以以O(1)求出区间[i,j]的区间和 :
sum = [0]*(n+1) # 一般+1为了边界计算方便
for i in range(1, n+1):
sum[i] = sum[i-1] + nums[i-1]
题目:和为K的子数组
给定一个整数数组和一个整数 k,你需要找到该数组中和为 k 的连续的子数组的个数。
示例 1 :
输入:nums = [1,1,1], k = 2
输出: 2 , [1,1] 与 [1,1] 为两种不同的情况。
来源:力扣(LeetCode)
链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
def subarraySum(self, nums, k):
if not nums:
return 0
n = len(nums)
sum = 0 # 前缀和
sum_dict = {0:1} # 记录前缀和数量
count = 0
for i in range(n):
sum += nums[i]
if sum_dict.get(sum - k):
count += sum_dict.get(sum - k) # +前缀数量
sum_dict[sum] = sum_dict.get(sum, 0) + 1
return count
2、二维前缀和
公式:
sum[i][j]存储左上角坐标为(0,0),右下角坐标为(i,j)的子矩阵的和。
sum[i][j] = matrix[i - 1][j - 1] + \sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]递推求值即可,两部分相加,减去重复计算部分。
value = sum[i][j] - sum[i - k][j] - \sum[i][j - k] + sum[i - k][j - k]可求得一个k * k大小子矩阵的和。
sum = [[0 for j in xrange(m + 1)] for i in xrange(n + 1)]
for i in xrange(1, n + 1):
for j in xrange(1, m + 1):
sum[i][j] = matrix[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]
题目1:滑动窗口矩阵的最大值
给定一个n * m矩阵数组和一个移动矩阵窗口(大小k * k),在每次迭代时将窗口从左上移到右下,在每次移动时找到窗口内的最大和。
如果答案不存在,则返回0。
来源:lintcode
链接:LintCode 炼码 - ChatGPT!更高效的学习体验!
def maxSlidingWindow2(self, matrix, k):
n = len(matrix)
if n == 0 or n < k:
return 0
m = len(matrix[0])
if m == 0 or m < k:
return 0
sum = [[0 for j in xrange(m + 1)] for i in xrange(n + 1)]
for i in xrange(1, n + 1):
for j in xrange(1, m + 1):
sum[i][j] = matrix[i - 1][j - 1] + sum[i - 1][j] + sum[i][j - 1] - sum[i - 1][j - 1]
max_value = -0x80000000
for i in xrange(k, n + 1):
for j in xrange(k, m + 1):
value = sum[i][j] - sum[i - k][j] - sum[i][j - k] + sum[i - k][j - k]
if value > max_value:
max_value = value
return max_value
题目2:元素和为目标值的子矩阵数量
给出矩阵 matrix
和目标值 target
,返回元素总和等于目标值的非空子矩阵的数量。
子矩阵 x1, y1, x2, y2
是满足 x1 <= x <= x2
且 y1 <= y <= y2
的所有单元 matrix[x][y]
的集合。
如果 (x1, y1, x2, y2)
和 (x1', y1', x2', y2')
两个子矩阵中部分坐标不同(如:x1 != x1'
),那么这两个子矩阵也不同。
来源:力扣(LeetCode)
链接:力扣(LeetCode)官网 - 全球极客挚爱的技术成长平台
与上一个题的区别:
- 窗口大小不确定,窗口大小k=1~m
- 窗口不是k*k的,有可能是长方形窗口
前缀和优化:
枚举上边界,处理子矩阵,转换成在一行内, 求一个数组连续子串和为target的问题了,也就是一维前缀和。
def numSubmatrixSumTarget(matrix, target):
if not matrix or not matrix[0]:
return None
n, m = len(matrix), len(matrix[0])
count = 0
for start_row in range(n):
arr = [0] * m # 一行一行累积求和,用一维和代替二维和
for i in range(start_row, n):
sum_hash = {} # 存储前缀和的数量
sum = 0 # 前缀和
for j in range(m):
arr[j] += matrix[i][j]
sum += arr[j]
if sum == target: # 整个矩阵和等于目标值
count += 1
if sum - target in sum_hash: # 子矩阵和等于目标值
count += sum_hash[sum - target]
sum_hash[sum] = sum_hash.get(sum, 0) + 1
return count