Sliding Window Maximum Solution
Given an array nums and a window size k, return the maximum value in each window as it slides from left to right.
Problem Statement
You are given an array of integers nums and an integer k representing the size of a sliding window that moves from left to right. Return an array containing the maximum element in each window position.
Brute Force β O(n Β· k)
For each window position, scan all k elements to find the maximum. Simple but slow when k is large.
| Metric | Value |
|---|---|
| Time | O(n Β· k) |
| Space | O(1) β excluding output |
Monotonic Deque β O(n)
Maintain a deque (double-ended queue) of indices in decreasing order of their values. The front of the deque always holds the index of the current window's maximum. When adding a new element, remove all smaller elements from the back (they'll never be the max while the new element exists). Remove the front if it's outside the window.
- Step 1: Create an empty deque storing indices.
- Step 2: For each index
i: - Step 3: Remove front if it's outside the window:
deque.peekFirst() < i - k + 1. - Step 4: Remove all back elements with values β€
nums[i](they're useless). - Step 5: Push
ito back of deque. - Step 6: If
i β₯ k - 1(window is full):result.add(nums[deque.peekFirst()]).
- The deque is always in decreasing order of values.
- Every time we add a new element, we evict all smaller elements from the back β maintaining the monotonic property.
- This ensures the front is always the max, and each element is pushed/popped at most once β O(n) total.
Visual Walkthrough
Trace through nums = [1, 3, -1, -3, 5, 3, 6, 7], k = 3:
- Each index is pushed once and popped at most once. Total operations across all iterations = O(n).
- The deque front always holds the window maximum β O(1) to read.
Code β Java & Python
Complexity Analysis
| Approach | Time | Space | Trade-off |
|---|---|---|---|
| Brute Force | O(n Β· k) | O(1) | Scan k elements per window |
| Max-Heap (PQ) | O(n log n) | O(n) | Lazy deletion from heap; log n per op |
| Monotonic Deque β optimal | O(n) | O(k) | Each element pushed/popped once. Deque holds at most k. |
- A max-heap gives O(log n) per insert/delete β but removing elements that have left the window requires either lazy deletion (mark as stale and skip when popped) or a tree-based structure.
- The monotonic deque is simpler and strictly O(n): each element enters and leaves at most once.
Edge Cases & Pitfalls
| Case | Input | Why It Matters |
|---|---|---|
| k == 1 | [1, 3, -1] | Each element is its own window β output equals input. |
| k == n | [1, 3, -1] | Single window β output is [max(nums)] = [3]. |
| All same | [5, 5, 5], k=2 | Deque always has one element. Output is [5, 5]. |
| Descending | [4, 3, 2, 1], k=2 | Deque front is always the leftmost (largest). Output: [4, 3, 2]. |
| Ascending | [1, 2, 3, 4], k=2 | Each new element evicts all prior. Deque always size 1. Output: [2, 3, 4]. |
| Negative values | [-1, -3, -2], k=2 | Works the same β max of negatives. Output: [-1, -2]. |
< instead of <= when evicting from the back: nums[deque.peekLast()] <= nums[i]. Using strict < leaves equal elements in the deque, which is harmless for correctness but wastes space. Using <= keeps the deque minimal and ensures only the rightmost occurrence of equal values survives β cleaner and slightly more efficient.