Skip to content

Commit

Permalink
Create 0862. 和至少为 K 的最短子数组.md
Browse files Browse the repository at this point in the history
  • Loading branch information
itcharge committed Jan 8, 2024
1 parent 0209deb commit 9ad7c09
Showing 1 changed file with 102 additions and 0 deletions.
102 changes: 102 additions & 0 deletions Solutions/0862. 和至少为 K 的最短子数组.md
@@ -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)$。

0 comments on commit 9ad7c09

Please sign in to comment.