-
Notifications
You must be signed in to change notification settings - Fork 1.2k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
1 changed file
with
102 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,102 @@ | ||
# [0862. 和至少为 K 的最短子数组](https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/) | ||
|
||
- 标签:队列、数组、二分查找、前缀和、滑动窗口、单调队列、堆(优先队列) | ||
- 难度:困难 | ||
|
||
## 题目链接 | ||
|
||
- [0862. 和至少为 K 的最短子数组 - 力扣](https://leetcode.cn/problems/shortest-subarray-with-sum-at-least-k/) | ||
|
||
## 题目大意 | ||
|
||
**描述**:给定一个整数数组 $nums$ 和一个整数 $k$。 | ||
|
||
**要求**:找出 $nums$ 中和至少为 $k$ 的最短非空子数组,并返回该子数组的长度。如果不存在这样的子数组,返回 $-1$。 | ||
|
||
**说明**: | ||
|
||
- **子数组**:数组中连续的一部分。 | ||
- $1 \le nums.length \le 10^5$。 | ||
- $-10^5 \le nums[i] \le 10^5$。 | ||
- $1 \le k \le 10^9$。 | ||
|
||
**示例**: | ||
|
||
- 示例 1: | ||
|
||
```python | ||
输入:nums = [1], k = 1 | ||
输出:1 | ||
``` | ||
|
||
- 示例 2: | ||
|
||
```python | ||
输入:nums = [1,2], k = 4 | ||
输出:-1 | ||
``` | ||
|
||
## 解题思路 | ||
|
||
### 思路 1:前缀和 + 单调队列 | ||
|
||
题目要求得到满足和至少为 $k$ 的子数组的最短长度。 | ||
|
||
先来考虑暴力做法。如果使用两重循环分别遍历子数组的开始和结束位置,则可以直接求出所有满足条件的子数组,以及对应长度。但是这种做法的时间复杂度为 $O(n^2)$。我们需要对其进行优化。 | ||
|
||
#### 1. 前缀和优化 | ||
|
||
首先对于子数组和,我们可以使用「前缀和」的方式,方便快速的得到某个子数组的和。 | ||
|
||
对于区间 $[left, right]$,通过 $pre\underline{}sum[right + 1] - prefix\underline{}cnts[left]$ 即可快速求解出区间 $[left, right]$ 的子数组和。 | ||
|
||
此时问题就转变为:是否能找到满足 $i > j$ 且 $pre\underline{}sum[i] - pre\underline{}sum[j] \ge k$ 两个条件的子数组 $[j, i)$?如果能找到,则找出 $i - j$ 差值最小的作为答案。 | ||
|
||
#### 2. 单调队列优化 | ||
|
||
对于区间 $[j, i)$ 来说,我们应该尽可能的减少不成立的区间枚举。 | ||
|
||
1. 对于某个区间 $[j, i)$ 来说,如果 $pre\underline{}sum[i] - pre\underline{}sum[j] \ge k$,那么大于 $i$ 的索引值就不用再进行枚举了,不可能比 $i - j$ 的差值更优了。此时我们应该尽可能的向右移动 $j$,从而使得 $i - j$ 更小。 | ||
2. 对于某个区间 $[j, i)$ 来说,如果 $pre\underline{}sum[j] \ge pre\underline{}sum[i]$,对于任何大于等于 $i$ 的索引值 $r$ 来说,$pre\underline{}sum[r] - pre\underline{}sum[i]$ 一定比 $pre\underline{}sum[i] - pre\underline{}sum[j]$ 更小且长度更小。此时 $pre\underline{}sum[j]$ 可以直接忽略掉。 | ||
|
||
因此,我们可以使用单调队列来保存单调递增的 $pre\underline{}sum[x]$ 值的下标。 | ||
|
||
对于每一个位置 $i$ 我们可以判断其之前存入在单调队列中的 $pre\underline{}sum[j]$ 值,如果 $pre\underline{}sum[i] - pre\underline{}sum[j] \ge k$,则更新答案,并将 $j$ 从队头位置弹出。直到 $pre\underline{}sum[i] - pre\underline{}sum[j] < k$ 时为止。 | ||
|
||
如果队尾 $pre\underline{}sum[j] \ge pre\underline{}sum[i]$,那么 $$ | ||
|
||
使用一重循环遍历 $i$,对于 $pre\underline{}sum[i]$,我们希望使用某个数据结构,能够使得在满足 $pre\underline{}sum[i] - pre\underline{}sum[j] \ge k$ 当前前提下,能够尽可能的向右移动 $j$,从而使得 $i - j$ 最小。 | ||
|
||
### 思路 1:代码 | ||
|
||
```Python | ||
class Solution: | ||
def shortestSubarray(self, nums: List[int], k: int) -> int: | ||
size = len(nums) | ||
|
||
pre_sum = [0 for _ in range(size + 1)] | ||
for i in range(size): | ||
pre_sum[i + 1] = pre_sum[i] + nums[i] | ||
|
||
ans = float('inf') | ||
queue = collections.deque() | ||
|
||
for i in range(size + 1): | ||
# 优化 1 | ||
while queue and pre_sum[i] - pre_sum[queue[0]] >= k: | ||
ans = min(ans, i - queue.popleft()) | ||
# 优化 2 | ||
while queue and pre_sum[queue[-1]] >= pre_sum[i]: | ||
queue.pop() | ||
queue.append(i) | ||
|
||
if ans == float('inf'): | ||
return -1 | ||
return ans | ||
``` | ||
|
||
### 思路 1:复杂度分析 | ||
|
||
- **时间复杂度**:$O(n)$,其中 $n$ 为数组 $nums$ 的长度。 | ||
- **空间复杂度**:$O(n)$。 | ||
|