关于前缀和概念的介绍,可以看这篇文章
基本的思想就是对于一个长度为n的数组nums,如果我们要求其中下标l到r之间的元素的总和。
用前缀和的思想就是,先记录s[1],s[2]…s[n]的值,然后在求l到r之间元素和的时候,用s[r]-s[l-1]即可。
下面进入leetcode实战
题目有
LC 1 两数之和
LC 560 和为k的子数组的个数
LC 325 和等于k的最长子数组长度
LC 1658 将x 减到0的最小操作数
LC 209长度最小的子数组
leetcode 1 两数之和
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
d={} # key=nums[i] value=i
for i in range(len(nums)):
if target-nums[i] in d:
return [d[target-nums[i]],i]
else:
d[nums[i]]=i
leetcode 560 和为k的子数组 的个数
这里是典型的前缀和题目
class Solution:
def subarraySum(self, nums: List[int], k: int) -> int:
curr_sum = 0
res = 0
count = {}
for num in nums:
curr_sum += num
if curr_sum == k:
#这里表示s[:i]全部和==k
res += 1
if curr_sum - k in count:
#这里表示只有s[j:i]一部分等于k,
#这种情况和上一种是互相独立的,并不是else
res += count[curr_sum - k]
count[curr_sum] = count.get(curr_sum, 0) + 1
#这里包含两种情况,curr_sum之前存在于count中和不存在于count中
#用上面这一句话表达了
return res
leetcode 325 和等于 k 的最长子数组长度
给定一个数组 nums 和一个目标值 k,找到和等于 k 的最长连续子数组长度。如果不存在任意一个符合要求的子数组,则返回 0。
输入: nums = [1,-1,5,-2,3], k = 3
输出: 4
解释: 子数组 [1, -1, 5, -2] 和等于 3,且长度最长。
class Solution:
def maxSubArrayLen(self, nums: List[int], k: int) -> int:
# 这题跟leetcode 560类似 只是dict中的value不一样
# leetcode 560记录个数,这里记录index
n=len(nums)
res,curr_sum=0,0
length_dict={} #key=curr_sum value=length
for i in range(n):
curr_sum+=nums[i]
# print(res, length_dict)
if curr_sum==k:
res=i+1
if curr_sum not in length_dict:
length_dict[curr_sum]=i #需要把最左边的记录下来,以便下面代码的最大的记下来
if curr_sum-k in length_dict:
res=max(res,i-length_dict[curr_sum-k])
return res
leetcode 1658 将 x 减到 0 的最小操作数
给你一个整数数组 nums 和一个整数 x 。每一次操作时,你应当移除数组 nums 最左边或最右边的元素,然后从 x 中减去该元素的值。请注意,需要 修改 数组以供接下来的操作使用。
输入:nums = [3,2,20,1,1,3], x = 10
输出:5
解释:最佳解决方案是移除后三个元素和前两个元素(总共 5 次操作),将 x 减到 0 。
class Solution:
def minOperations(self, nums: List[int], x: int) -> int:
# 假设数组和是s 那么这里就是要求一个和为s-x的最长连续子数组
# 而 n-最长数组长度 就是最小操作次数
# 参考leetcode 325
s=sum(nums)
n=len(nums)
target=s-x
idx_dict={}
curr_sum=0
res=float('-inf')
for i in range(n):
curr_sum+=nums[i]
if curr_sum==target:
res=i+1
if curr_sum not in idx_dict:
idx_dict[curr_sum]=i+1
if curr_sum-target in idx_dict:
res=max(res,i+1-idx_dict[curr_sum-target])
# print(res, idx_dict)
if res==float('-inf'):
return -1
return n-res
leetcode 209长度最小的子数组
给定一个含有 n 个正整数的数组和一个正整数 target 。
找出该数组中满足其和 ≥ target 的长度最小的 连续子数组 [numsl, numsl+1, …, numsr-1, numsr] ,并返回其长度。如果不存在符合条件的子数组,返回 0 。
class Solution:
def minSubArrayLen(self, target: int, nums: List[int]) -> int:
# 这里是>=target 用两数之和那种前缀和+哈希方法了 需要额外加一层循环 复杂度n^2
# 用left+right 双指针 可以达到left和right同时遍历走完 不需要内嵌
n=len(nums)
curr_sum=0
left,right=0,0
res=n+1
while right<n:
while curr_sum<target and right<n:
curr_sum+=nums[right]
right+=1
while curr_sum>=target and left<right:
res=min(res,right-left)
curr_sum-=nums[left]
left+=1
return res if res!=n+1 else 0