How to Explain Sliding Window Maximum in a Coding Interview (Step-by-Step)
Sliding Window Maximum is the problem that unlocks the monotonic deque pattern — one of the most powerful and least understood tools in the interview toolkit. The brute force is obvious. The O(n) solution requires one sharp insight: useless elements should never even make it onto the deque.
In this post, you’ll learn exactly how to walk an interviewer through LeetCode 239: what a monotonic deque is and why it fits this problem, the three deque operations in the right order, full step-by-step tracing, and every edge case worth calling out.
Problem Statement
You are given an array of integers nums and an integer k. There is a sliding window of size k moving from the left to the right of the array. You can only see the k numbers in the window. Each time the window moves one position to the right, return the maximum value in the window.
Return the max sliding window as an array.
Examples
Output: [3,3,5,5,6,7]
Window positions:
[1 3 -1 -3 5 3 6 7] → max = 3
[ 1 3 -1 -3 5 3 6 7] → max = 3
[ 1 3 -1 -3 5 3 6 7] → max = 5
[ 1 3 -1 -3 5 3 6 7] → max = 5
[ 1 3 -1 -3 5 3 6 7] → max = 6
[ 1 3 -1 -3 5 3 6 7] → max = 7
Output: [1]
Constraints
1 <= nums.length <= 105-104 <= nums[i] <= 1041 <= k <= nums.length
What Is the Problem?
“I have a fixed-size window of widthksliding across the array one step at a time. For each window position, I need to report the maximum element. The output hasn - k + 1values. The challenge is doing this faster than O(nk) — brute-force scanning each window independently.”
Naming the output size n - k + 1 and the brute-force cost O(nk) immediately shows the interviewer you’ve framed the problem correctly before reaching for an approach.
Step 1: Compare Approaches Before Jumping to Code
“A max-heap works but requires lazy deletion — when an element leaves the window, we can’t efficiently remove it from the middle of a heap. The monotonic deque avoids this entirely: we only store indices of elements that could still be the maximum, and we evict them eagerly as they become useless.”
The Key Insight — Useless Elements Never Belong on the Deque
When a new element nums[i] enters the window, any existing element in the deque that is smaller than or equal to nums[i] can be discarded immediately — forever. Here’s why: those smaller elements are both older and smaller. They will leave the window before the new element does, and they will never be the maximum while the new element is present. They are permanently useless.
By discarding them from the back before pushing the new element, the deque stays monotonically decreasing from front to back. The front is always the largest element in the current window.
“Think of it this way: if I’m standing in a queue and someone taller than me joins behind me, I’ll never be seen. For maximum purposes I’m invisible for the rest of my time in the window. The deque just formalises that intuition — remove anyone who can never be the answer.”
Monotonic Deque Properties
1. Store indices, not values. We need indices to check whether the front element has slid out of the window boundary (deque[0] < i - k + 1). Values alone can’t tell us this.
2. Deque is monotonically decreasing by value. nums[deque[0]] >= nums[deque[1]] >= ... at all times. This guarantees the front is always the window maximum.
3. All indices in the deque are within the current window. The front is evicted as soon as it falls outside [i - k + 1, i].
Step 2: Explain the Algorithm in Plain English
The deque stores indices into nums, not values. It will always be maintained in decreasing order of their corresponding values.
Before processing index i, check if deque[0] (the current maximum’s index) has fallen outside the window — i.e., deque[0] < i - k + 1. If so, pop it from the front.
While the deque is non-empty and nums[deque[-1]] <= nums[i], pop from the back. These elements are permanently useless — they’re older and no larger than the incoming element.
Append index i to the back of the deque. Once the first full window is formed (i >= k - 1), append nums[deque[0]] to the result — the front of the deque is always the current window maximum.
Step-by-Step Deque Trace — nums = [1,3,-1,-3,5,3,6,7], k = 3
The deque stores indices. Values shown in parentheses for readability. Green = front (current max), blue = rest of deque.
Step 3: Talk Through the Code
from collections import deque
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
dq = deque() # Stores indices; front = index of current window max
result = []
for i in range(len(nums)):
# 1. Evict front if it has slid out of the window
if dq and dq[0] < i - k + 1:
dq.popleft()
# 2. Evict from back all indices whose values are <= nums[i]
# They can never be the maximum while nums[i] is in the window
while dq and nums[dq[-1]] <= nums[i]:
dq.pop()
# 3. Push current index
dq.append(i)
# 4. Record max once first full window is formed
if i >= k - 1:
result.append(nums[dq[0]])
return result
Three things worth narrating explicitly: we store indices not values (so we can check window boundaries); the front eviction happens before the back eviction (order matters — clean the front first so it’s always valid when we read it); and we only record a result once i >= k - 1, meaning the first full window has been formed.
Step 4: Highlight Edge Cases
“Every element is its own window. The deque always holds exactly one index, immediately evicted and replaced. The output is identical to the input. Handled correctly with no special casing.”
“Only one window position exists. The deque accumulates indices until the end and the front holds the global maximum index. We record exactly one result. Output is a single-element array.”
“No back-evictions ever occur — each new element is smaller than the back of the deque. The front eviction fires every step once the window is full. The deque always has up to k elements, and the front is always the leftmost (largest) element in the window.”
“Every new element triggers back-evictions of everything currently in the deque. The deque always has exactly one element — the most recently added index. The front is always the rightmost (and largest) element of the window.”
“When nums[dq[-1]] <= nums[i], equal values are also evicted from the back. This is correct — the older equal element can never outlive the newer one, and keeping it would leave a stale index in the deque unnecessarily.”
Step 5: State Time and Space Complexity
| Dimension | Complexity | Reason |
|---|---|---|
| Time | O(n) | Each index is pushed onto the deque exactly once and popped at most once — total work across all iterations is O(n) |
| Space | O(k) | The deque holds at most k indices at any time — one per window position |
“The inner while loop for back-eviction looks like it could make this O(n²), but it can’t. Every index is pushed onto the deque exactly once and popped at most once — either from the back during eviction, or from the front when it leaves the window. So the total number of push + pop operations across the entire run is at most 2n — O(n) overall. This is the same amortised argument as for the Longest Consecutive Sequence inner loop.”
Interview Checklist for Sliding Window Maximum
- Restate — “max of every k-element window; output has n−k+1 values”
- Name the brute-force cost O(nk) and explain why it’s too slow
- Mention max-heap O(n log k) and why lazy deletion makes it messier than it looks
- Introduce the monotonic deque — decreasing front-to-back, front is always the window max
- State the core insight — elements smaller than the incoming value can never be the max; discard them eagerly
- Store indices not values — needed to check window boundary expiry
- Describe the three operations in order: front eviction → back eviction → push → record
- Explain why back-eviction uses <= (equal values: older one is never needed again)
- Justify O(n) time with the amortised argument — each index pushed and popped at most once
- State O(k) space for the deque
- Call out edge cases: k=1, k=n, strictly decreasing, strictly increasing, duplicates
Related Problems to Practice
If you can solve Sliding Window Maximum cleanly, try these next:
