给你一个整数数组 nums 和一个整数 k ,找出 nums 中和至少为 k 的 最短非空子数组 ,并返回该子数组的长度。如果不存在这样的 子数组 ,返回 -1 。
子数组 是数组中 连续 的一部分。
示例 1:
输入:nums = [1], k = 1
输出:1
示例 2:
输入:nums = [1,2], k = 4
输出:-1
示例 3:
输入:nums = [2,-1,2], k = 3
输出:3
提示:
1 <= nums.length <= 105
-105 <= nums[i] <= 105
1 <= k <= 109
class Solution:
def shortestSubarray(self, nums: List[int], k: int) -> int:
preSumArr = [0]
res = len(nums) + 1
for num in nums:
preSumArr.append(preSumArr[-1] + num)
q = deque()
for i, curSum in enumerate(preSumArr):
while q and curSum - preSumArr[q[0]] >= k:
res = min(res, i - q.popleft())
while q and preSumArr[q[-1]] >= curSum:
q.pop()
q.append(i)
return res if res < len(nums) + 1 else -1
心得体会:
题目要求计算 \textit{nums}nums 中,和大于等于 kk 的最短子数组的长度。可以以 \textit{nums}nums 的每一个下标作为候选子数组的起点下标,都计算满足条件的最短子数组的长度,然后再从这些长度中找出最小值即可。
遍历 \textit{preSumArr}preSumArr 数组,访问过的前缀和先暂存在某种集合 qq 中。根据前缀和数组的性质,后访问到的某个前缀和 \textit{preSumArr}[j]preSumArr[j] 减去之前访问到的某个前缀和 \textit{preSumArr}[i]preSumArr[i](j \gt ij>i)即为 \textit{nums}nums 中某段子数组的和。因此,每次访问到某个前缀和 \textit{preSumArr}[j]preSumArr[j] 时,可以用它尝试减去集合 qq 中所有已经访问过的前缀和。当某个 qq 中的前缀和 \textit{preSumArr}[i]preSumArr[i],第一次出现 \textit{preSumArr}[j] - \textit{preSumArr}[i] \geq kpreSumArr[j]−preSumArr[i]≥k 时,这个下标 ii 就找到了以它为起点的最短子数组的长度 j-ij−i。此时,可以将它从 qq 中移除,后续即使还有以它为起点的满足条件的子数组,长度也会大于当前的长度。当一个前缀和 \textit{preSumArr}[j]preSumArr[j] 试减完 qq 中的元素时,需要将它也放入 qq 中。将它放入 qq 前, qq 中可能存在比 \textit{preSumArr}[j]preSumArr[j] 大的元素,而这些元素和 \textit{preSumArr}[j]preSumArr[j] 一样,只能作为再后续访问到的某个前缀和 \textit{preSumArr}[h]preSumArr[h] 的减数。而作为减数时,更大的值只会让不等式 \textit{preSumArr}[h] - \textit{preSumArr}[i] \geq kpreSumArr[h]−preSumArr[i]≥k 更难满足。即使都满足,后访问到的值也可以带来更短的长度。 因此,在把 \textit{preSumArr}[j]preSumArr[j] 放入 qq 时,需要将 qq 中大于等于 \textit{preSumArr}[j]preSumArr[j] 的值也都移除。