Sliding Window Maximum (Leetcode 239)
Shortest Subarray with Sum at Least K (Leetcode 862)
Pattern: prefix sum + monotonic deque
Idea:
Variations :
π» Code
from collections import deque
def shortestSubarray(nums, k):
n = len(nums)
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
dq = deque()
ans = n + 1
for i in range(n + 1):
while dq and prefix[i] - prefix[dq[0]] >= k:
ans = min(ans, i - dq.popleft())
while dq and prefix[dq[-1]] >= prefix[i]:
dq.pop()
dq.append(i)
return ans if ans <= n else -1
Time complexity - O(n)
Aux. Space complexity - O(n)
Shortest Subarray with Sum at Least K (Leetcode 862)
Tags: #Arrays #PrefixSum #MonotonicQueue #Deque #SlidingWindow #Greedy #Interview-Pattern #LeetCode #FAANG
Problem Statement
Given an integer array nums (can contain negative numbers) and an integer K, return the length of the shortest non-empty subarray whose sum is at least K. If no such subarray exists, return -1.
The presence of negative numbers is what makes this problem significantly harder than ordinary sliding window problems.
Why Sliding Window Fails
Sliding window relies on a monotonic property:
-
Expanding β sum increases
-
Shrinking β sum decreases
With negative numbers, this breaks.
Example:
nums = [2, -1, 2], K = 3
Removing -1 actually increases the sum, so thereβs no deterministic rule for moving pointers.
Conclusion: We need Prefix Sum + Monotonic Deque.
Key Idea
Let the prefix sum array be:
-
P[0] = 0 -
P[i] = sum of first i elements
For a subarray (j ... i-1):
Subarray Sum = P[i] β P[j]
We need:
P[i] β P[j] β₯ K
For every i, we want the largest possible j (closest to i) that still satisfies the inequality, because that minimizes:
Length = i β j
The deque efficiently maintains the best candidate prefix indices.
Intuition (The WHY)
The deque stores indices of prefix sums in increasing order of prefix values.
Two observations make the algorithm work:
1. Front gives the shortest valid answer
If:
P[i] β P[dq[0]] β₯ K
then weβve found a valid subarray.
Since dq[0] is the earliest feasible prefix, removing it may reveal an even later prefix that gives an even shorter subarray.
So we repeatedly pop from the front.
2. Larger prefix sums dominate smaller ones
Suppose:
| Index | Prefix |
|---|---|
| 2 | 8 |
| 5 | 6 |
Index 5 is always better because:
-
Smaller prefix sum (
6 < 8) -
Later index (shorter distance)
The prefix 8 is permanently useless.
Therefore we remove it from the back.
This creates a monotonically increasing deque of prefix sums.
Approach β Prefix Sum + Monotonic Deque
Algorithm
-
Build prefix sums.
-
Iterate through each prefix index.
-
While the front forms a valid subarray, update the answer and pop it.
-
While the current prefix is smaller than the backβs prefix, pop from the back.
-
Push the current index.
Python Solution
from collections import deque
def shortestSubarray(nums, k):
n = len(nums)
prefix = [0] * (n + 1)
for i in range(n):
prefix[i + 1] = prefix[i] + nums[i]
dq = deque()
ans = n + 1
for i in range(n + 1):
while dq and prefix[i] - prefix[dq[0]] >= k:
ans = min(ans, i - dq.popleft())
while dq and prefix[dq[-1]] >= prefix[i]:
dq.pop()
dq.append(i)
return ans if ans <= n else -1
Dry Run
nums = [2, -1, 2], K = 3
Prefix sums:
| Index | Prefix |
|---|---|
| 0 | 0 |
| 1 | 2 |
| 2 | 1 |
| 3 | 3 |
Deque stores indices.
| i | Prefix | Action | Deque |
|---|---|---|---|
| 0 | 0 | Push | [0] |
| 1 | 2 | Push | [0,1] |
| 2 | 1 | Remove 1, Push | [0,2] |
| 3 | 3 | Valid β Answer=3 | [2,3] |
Result = 3
Subarray:
[2, -1, 2]
Visualizing the Two While Loops
First While β Find Valid Answers
Current Prefix = 15
Deque Prefixes:
Index : 0 2 5
Prefix: 1 4 8
If 15 β 1 β₯ K, then length is valid.
Pop it and try the next one:
15 β 4 β₯ K ?
A later prefix may produce an even shorter answer.
Second While β Remove Dominated Prefixes
Current Prefix = 6
Deque Back Prefix = 9
Since 6 < 9:
-
Smaller prefix is always better.
-
Current index is also later.
So prefix 9 is useless forever.
Pop it.
Why the Deque Is Monotonic
The deque maintains:
Prefix values:
2
5
8
11
When a new prefix 6 arrives:
2
5
6
The 8 and 11 disappear because they are dominated.
This guarantees every prefix enters and leaves the deque once, giving linear complexity.
Complexity
| Metric | Value |
|---|---|
| Time | O(n) |
| Auxiliary Space | O(n) |
-
Prefix array:
O(n) -
Deque: up to
O(n)
Important Variations
-
Leetcode 239 β Sliding Window Maximum β Monotonic deque over values.
-
Longest Subarray with Sum K β Prefix sum + first occurrence hashmap.
-
Count Subarrays with Sum K β Prefix sum + frequency hashmap.
-
Constrained Subsequence Sum β Monotonic deque over DP values.
The reusable pattern is:
Prefix Sum + Monotonic Queue whenever negatives destroy ordinary sliding window.
Common Mistakes / Quirks
1. Forgetting the initial prefix 0
Prefix array must start with:
prefix[0] = 0
Otherwise subarrays beginning at index 0 are missed.
2. Using > instead of >=
The condition is:
prefix[i] - prefix[dq[0]] >= k
Equal is also valid.
3. Removing from the back before checking the front
The correct order is:
-
Check valid answers (front)
-
Remove dominated prefixes (back)
Changing the order can discard candidates prematurely.
4. Storing prefix values instead of indices
Always store indices so the length can be computed as:
i - dq[0]
Key Takeaways / Pattern Recognition
-
Negative numbers + shortest/optimal subarray β Think Prefix Sum + Monotonic Deque.
-
The deque is monotonic by prefix sum, not by original array values.
-
Front pops compute answers; back pops remove dominated candidates.
-
This is the natural progression after mastering:
-
Prefix Sum + HashMap (count/longest)
-
Monotonic Deque (sliding maximum)
-
-
Leetcode 862 is one of the highest-value FAANG problems because it combines both patterns into a single linear-time solution.