和至少为 K 的最短子数组
难度:
标签:
题目描述
代码结果
运行时间: 204 ms, 内存: 31.6 MB
/*
* Approach: In Java Stream API, we cannot directly use it to maintain the sliding window or deque structures.
* Therefore, we will outline the steps and implement them with standard Java collections while using Streams where possible.
*/
import java.util.*;
import java.util.stream.*;
public class ShortestSubarraySumStream {
public int shortestSubarray(int[] nums, int k) {
int n = nums.length;
long[] prefixSums = new long[n + 1];
IntStream.range(0, n).forEach(i -> prefixSums[i + 1] = prefixSums[i] + nums[i]);
int[] result = {Integer.MAX_VALUE};
Deque<Integer> deque = new LinkedList<>();
IntStream.range(0, n + 1).forEach(i -> {
while (!deque.isEmpty() && prefixSums[i] - prefixSums[deque.peekFirst()] >= k) {
result[0] = Math.min(result[0], i - deque.pollFirst());
}
while (!deque.isEmpty() && prefixSums[i] <= prefixSums[deque.peekLast()]) {
deque.pollLast();
}
deque.addLast(i);
});
return result[0] == Integer.MAX_VALUE ? -1 : result[0];
}
}
解释
方法:
此题解采用前缀和和单调队列的组合方法来解决问题。首先,使用前缀和数组来记录从数组开始到当前位置的所有元素的累加和,这样可以快速计算任意子数组的和。然后,使用一个单调递增的队列来存储前缀和数组的索引,队列中的每个元素代表一个潜在的最小起点位置。对于每个新的前缀和,我们首先从队尾开始移除那些不再可能成为最短子数组起点的元素(因为一个更小的前缀和已出现)。然后,从队首开始检查是否存在有效的子数组,即当前前缀和与队首索引对应的前缀和之差至少为k。如果存在,更新结果并弹出队首元素,因为更短的有效子数组不可能再以此队首元素为起点。最后,将当前前缀和的索引加入队列。这一过程持续到遍历完所有的前缀和,最后根据结果变量判断是否找到了满足条件的子数组。
时间复杂度:
O(n)
空间复杂度:
O(n)
代码细节讲解
🦆
在计算前缀和时,为什么要在列表的开始处加入一个初始值0?这样做有什么好处?
▷🦆
单调队列中移除队尾元素的条件是当前前缀和小于队尾对应的前缀和,这样做的目的是什么?
▷🦆
为什么在找到一个满足条件的子数组后要从队列中弹出队首元素?
▷🦆
如果输入数组中包含负数会怎样影响算法的执行?
▷