给你一个整数数组 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 <= 10^5-10^5 <= nums[i] <= 10^51 <= k <= 10^9
思路
这题如果不是顺着单调队列题单做下来,很难联想到要用单调队列来做。因为原始数组没有任性质。但是转化成前缀和之后,就会柳暗花明。
- 计算原数组的前缀和数组,记为 S
- 遍历 S,用合适的数据结构同时记录见到的最小值,次小值,等等
- 遍历至下标 i,首先更新上面的数据结构,记录新的最小值,次小值等等
- 如果
S[i] - 最小值 >= k,更新答案,且继续和次小值比较 - 否则,遍历下一个
- 如果
- 返回答案
而同时记录最值,次最值,次次最值正好可以用单调队列(双端队列)。
Code
class Solution {
public:
int shortestSubarray(vector<int>& nums, int k) {
const int n = nums.size();
vector<long> psum(n + 1, 0);
// for (int i = 0; i < n; ++i) {
// psum[i+1] = psum[i] + nums[i];
// }
std::partial_sum(nums.begin(), nums.end(), psum.begin() + 1);
int ans = n + 1;
deque<int> minq;
for (int i = 0; i <= n; ++i) {
// 维护单调性
while (!minq.empty() && psum[i] <= psum[minq.back()])
minq.pop_back();
minq.push_back(i);
// 更新答案+元素出队
while (!minq.empty() && psum[i] - psum[minq.front()] >= k) {
ans = min(ans, i - minq.front());
minq.pop_front();
}
}
return ans > n ? -1 : ans;
}
};