[Hard] 239. Sliding Window Maximum
You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the max sliding window.
Examples
Example 1:
Input: nums = [1,3,-1,-3,5,3,6,7], k = 3
Output: [3,3,5,5,6,7]
Explanation:
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
Example 2:
Input: nums = [1], k = 1
Output: [1]
Constraints
1 <= nums.length <= 10^5-10^4 <= nums[i] <= 10^41 <= k <= nums.length
Thinking Process
- Monotonic Deque: Maintain indices in decreasing order of values
- Maintain a window
[left, right]satisfying a constraint. - Expand
rightto grow; shrinkleftwhen invalid. - Fixed window: slide both pointers together.
Common Approaches
Typical techniques for this pattern:
| Approach | Time | Space | Notes |
|---|---|---|---|
| Fixed-size window (this problem) | O(n) | O(1) | Window size known upfront |
| Variable-size window | O(n) | O(1) | Expand/shrink until valid |
| Window + hash map | O(n) | O(k) | Track character/count frequencies |
| Deque window max | O(n) | O(k) | Monotonic deque for max/min in window |
Solution
Time Complexity: O(n) - Each element is added and removed at most once
Space Complexity: O(k) - Deque stores at most k elements
Use a deque (double-ended queue) to maintain indices of elements in decreasing order of their values. This allows O(1) access to the maximum element in the current window.
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> dq = new ArrayDeque<>();
int[] result = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
while (!dq.isEmpty() && dq.peekFirst() < i - k + 1) dq.pollFirst();
while (!dq.isEmpty() && nums[dq.peekLast()] <= nums[i]) dq.pollLast();
dq.offerLast(i);
if (i >= k - 1) result[i - k + 1] = nums[dq.peekFirst()];
}
return result;
}
}```
### Solution Explanation
**Approach:** Fixed-size window (this problem)
**Key idea:** 1. **Monotonic Deque**: Maintain indices in decreasing order of values
**How the code works:**
1. **Monotonic Deque**: Maintain indices in decreasing order of values
- Maintain a window `[left, right]` satisfying a constraint.
- Expand `right` to grow; shrink `left` when invalid.
- Fixed window: slide both pointers together.
**Walkthrough** — input `nums = [1,3,-1,-3,5,3,6,7], k = 3`, expected output `[3,3,5,5,6,7]`:
Window position Max
--------------- -----
[1 3 -1] -3 5 3 6 7 3
1 [3 -1 -3] 5 3 6 7 3
1 3 [-1 -3 5] 3 6 7 5
1 3 -1 [-3 5 3] 6 7 5
1 3 -1 -3 [5 3 6] 7 6
1 3 -1 -3 5 [3 6 7] 7
| Aspect | Complexity |
|--------|------------|
| **Time** | O(n) - Each element is added once and removed at most once |
| **Space** | O(k) - Deque stores at most k indices |
## Algorithm Breakdown
### 1. Initialize
```java
class Solution {
public int[] maxSlidingWindow(int[] nums, int k) {
Deque<Integer> dq = new ArrayDeque<>();
int[] result = new int[nums.length - k + 1];
for (int i = 0; i < nums.length; i++) {
while (!dq.isEmpty() && dq.peekFirst() < i - k + 1) dq.pollFirst();
while (!dq.isEmpty() && nums[dq.peekLast()] <= nums[i]) dq.pollLast();
dq.offerLast(i);
if (i >= k - 1) result[i - k + 1] = nums[dq.peekFirst()];
}
return result;
}
}```
- `rtn`: Result array to store maximums
- `q`: Deque storing indices in decreasing order of values
### 2. Remove Out-of-Window Indices
```cpp
while(!q.empty() && q.front() < i - k + 1) {
q.pop_front();
}
- Window starts at
i - k + 1 - Remove indices that are before the window start
3. Remove Smaller Elements
while(!q.empty() && nums[q.back()] < nums[i]) {
q.pop_back();
}
- Remove indices whose values are smaller than
nums[i] - These elements can never be the maximum in any future window
- Maintains decreasing order in deque
4. Add Current Index
q.push_back(i);
- Add current index to deque
5. Record Maximum
if(i >= k - 1) {
rtn.push_back(nums[q.front()]);
}
- When window is complete (i >= k - 1), add maximum to result
- Maximum is always at
q.front()
Complexity
| Aspect | Complexity | |——–|————| | Time | O(n) - Each element is added once and removed at most once | | Space | O(k) - Deque stores at most k indices |
Why O(n) Time?
- Each index is added to deque exactly once: O(n)
- Each index is removed from deque at most once: O(n)
- Total: O(n) operations
Why Monotonic Deque is Optimal
- Linear Time: Each element is processed exactly once
- Constant Operations: Deque operations (push, pop) are O(1) amortized
- Efficient Removal: Removing smaller elements early prevents unnecessary comparisons
- Direct Access: Maximum is always at front, no need to search
Common Mistakes
- k = 1: Each window has one element → return all elements
- k = nums.size(): Single window → return maximum of entire array
- All increasing:
[1,2,3,4,5]→ deque always has one element - All decreasing:
[5,4,3,2,1]→ deque contains all indices initially -
All same:
[3,3,3,3]→ all 3s in result - Forgetting to remove out-of-window indices: Must check
q.front() < i - k + 1 - Wrong comparison: Should be
nums[q.back()] < nums[i]not<=(handles duplicates) - Adding before window complete: Only add to result when
i >= k - 1 - Using values instead of indices: Deque should store indices to check window boundaries
Optimization Tips
Early Termination Check
// Optional: If k == 1, just return the array
if(k == 1) return nums;
// Optional: If k == nums.size(), return max element
if(k == nums.size()) {
return {*max_element(nums.begin(), nums.end())};
}
Memory Optimization
The deque approach is already optimal. For very large arrays, you could use a fixed-size array, but deque is more flexible and still O(k) space.
Related Problems
- 239. Sliding Window Maximum - This problem
- 480. Sliding Window Median - Find median instead of maximum
- 1438. Longest Continuous Subarray With Absolute Diff Less Than or Equal to Limit - Use two deques for min and max
- 2392. Build a Matrix With Conditions - Different application
- 862. Shortest Subarray with Sum at Least K - Monotonic deque for prefix sums
Pattern Recognition
This problem demonstrates the Monotonic Deque pattern:
- Maintain a deque with elements in monotonic order (increasing or decreasing)
- Remove elements that violate the monotonic property
- Use deque to efficiently query extreme values (min/max) in a sliding window
Applications:
- Sliding window maximum/minimum
- Next greater/smaller element
- Range queries in sliding windows
- Dynamic programming optimizations
Code Quality Notes
- Readability: Clear variable names (
qfor queue,rtnfor result) - Efficiency: Optimal time and space complexity
- Correctness: Handles all edge cases properly
- Maintainability: Well-structured code with clear comments
Implementation Details
Why Store Indices Instead of Values?
Storing indices allows us to:
- Check if an element is outside the window:
q.front() < i - k + 1 - Access the value:
nums[q.front()] - Compare values:
nums[q.back()] < nums[i]
Why Remove from Back?
We maintain decreasing order, so when we encounter a larger value:
- All smaller values at the back can never be maximum
- Removing from back maintains the monotonic property
- Front always contains the maximum index
Why Check i >= k - 1?
- Window of size
kstarting at indexicovers[i, i+k-1] - When
i = k - 1, window is[0, k-1](first complete window) - Before this, window is incomplete, so we don’t record maximum
This problem is a classic example of using a monotonic deque to efficiently solve sliding window problems. The key insight is maintaining a data structure that automatically keeps track of the maximum while efficiently removing outdated elements.
Key Takeaways
- Monotonic Deque: Maintain indices in decreasing order of values
- Remove Out-of-Window: Remove indices
i - k + 1from the front - Remove Smaller Elements: Remove indices whose values are smaller than current (they can never be maximum)
- Front is Maximum:
q.front()always points to the maximum element in current window - Amortized O(1): Each element is added and removed at most once
References
- LC 239: Sliding Window Maximum on LeetCode
- LeetCode Discuss — LC 239: Sliding Window Maximum
- LeetCode Editorial (may require premium)