Hello! Welcome back to our series on high-ROI interview patterns.
In our last lesson, we explored the monotonic stack, a clever tool for finding the "next greater/smaller element" in linear time. We saw how maintaining a sorted order within the stack allowed us to avoid inefficient nested loops. Today, we'll adapt that powerful concept to solve a different but related problem: finding the maximum in a sliding window.
Your goal for this lesson is to learn how to implement and apply a deque (double-ended queue) to solve problems like Sliding Window Maximum. This is a classic, hard-tagged interview problem (like LeetCode 239) that elegantly demonstrates the power of using the right data structure. Mastering this pattern is a strong signal to interviewers that you can devise efficient, non-obvious solutions.
1. Understanding the Problem and the Tool
The problem is "Sliding Window Maximum." You are given an array and a window size k. As this window slides from left to right, you need to report the maximum value within the window at each position.

The brute-force approach is to iterate through each of the n-k+1 windows and then loop through the k elements inside to find the maximum. This gives a time complexity of O(n * k), which is too slow for typical interview constraints. We need a linear time, O(n), solution.
To achieve this, we introduce the deque (pronounced "deck"), or double-ended queue. As its name implies, it's a sequence container that allows for efficient insertions and deletions at both its front and back ends. In Python, this is readily available as collections.deque. Operations like append, pop, appendleft, and popleft are all O(1).
2. The Monotonic Deque Strategy
The key to an O(n) solution is to avoid re-scanning the window. How can we find the maximum in O(1) time for each window? This is where the connection to our previous lesson comes in. We will maintain a monotonic deque.
Specifically, we'll use the deque to store the indices of elements from the current window. We will enforce a rule: the values corresponding to the indices in the deque must be in decreasing order from front to back.
Why does this work?
- The Maximum is Always at the Front: Because the deque is sorted by value in decreasing order, the index at the very front (
dq[0]) will always correspond to the largest element in our list of candidates. - Efficiently Discarding Useless Elements: If a new, larger element
nums[i]arrives, any smaller elements currently in the deque that come before it are now useless. They can never be the maximum in any future window that includesnums[i]. We can therefore pop them from the back.
Let's watch a video that explains this core idea and contrasts it with the brute-force method.
Sliding Window Maximum - Monotonic Queue - Leetcode 239
The video 'Sliding Window Maximum' by NeetCode provides an excellent explanation of the problem and the intuition behind using a monotonic deque for an optimal solution.
Please watch from the beginning until 05:31. Focus on two key points: why the brute-force O(n*k) approach is inefficient, and the logic for using a decreasingly ordered deque to eliminate elements that can no longer be the maximum.
3. The Algorithm in Detail
Let's formalize the step-by-step logic for building our solution.
- Initialize an empty deque
dq(which will store indices) and an empty listresult. - Iterate through the input array
numsfrom left to right with indexi. For each elementnums[i]:- Clean up the front: Check if the index at the front of the deque is now out of the current window's bounds (i.e.,
dq[0] <= i - k). If it is,popleft()to remove it. - Clean up the back: While the deque is not empty and the current element
nums[i]is greater than the element at the index at the back of the deque (nums[dq[-1]]),pop()from the back. This removes all smaller elements that are now irrelevant. - Add the new element:
append()the current indexito the deque. - Record the maximum: If the window is fully formed (i.e.,
i >= k - 1), the maximum for this window is the element at the deque's front index. Appendnums[dq[0]]to theresultlist.
- Clean up the front: Check if the index at the front of the deque is now out of the current window's bounds (i.e.,
- Return the
resultlist.
To solidify this process, let's follow a detailed dry run. The following article provides a fantastic visual walkthrough.
Sliding Window Maximum - Tutorial
The article 'Sliding Window Maximum - Tutorial' from takeuforward.org has a great set of images illustrating the state of the deque as the window slides. It reinforces the algorithm we just discussed.
Read the 'Optimal Approach' section. Pay close attention to the sequence of six images that show the deque's state for the array [4,0,-1,3,5,3,6,8] with k=3. This will help you visualize the algorithm's three main steps: cleaning the front, cleaning the back, and adding the result.
Test your understanding!
Let's trace the algorithm for a simple case.
Input: nums = [8, 7, 6, 9], k = 2
What will be the state of dq (storing indices) and result after each iteration i?
Show answer
Initial state: dq = deque([]), result = []
-
i = 0,
nums[0] = 8:- Front cleanup: (skip,
dqempty) - Back cleanup: (skip,
dqempty) - Add index:
dq = deque([0]) - Record result: (skip,
i < k-1)
- Front cleanup: (skip,
-
i = 1,
nums[1] = 7:- Front cleanup: (skip,
dq[0]is not_ <= 1-2) - Back cleanup:
7 > nums[dq[-1]](7 > 8) is false. - Add index:
dq = deque([0, 1]) - Record result:
i >= k-1is true.result.append(nums[dq[0]])->result.append(8). - State:
dq = deque([0, 1]),result = [8]
- Front cleanup: (skip,
-
i = 2,
nums[2] = 6:- Front cleanup:
dq[0] <= i - k(0 <= 2 - 2) is true.dq.popleft().dqis nowdeque([1]). - Back cleanup:
6 > nums[dq[-1]](6 > 7) is false. - Add index:
dq = deque([1, 2]) - Record result:
i >= k-1is true.result.append(nums[dq[0]])->result.append(7). - State:
dq = deque([1, 2]),result = [8, 7]
- Front cleanup:
-
i = 3,
nums[3] = 9:- Front cleanup:
dq[0] <= i - k(1 <= 3 - 2) is true.dq.popleft().dqis nowdeque([2]). - Back cleanup:
9 > nums[dq[-1]](9 > 6) is true.dq.pop().dqis nowdeque([]). - Add index:
dq = deque([3]) - Record result:
i >= k-1is true.result.append(nums[dq[0]])->result.append(9). - State:
dq = deque([3]),result = [8, 7, 9]
- Front cleanup:
Final Result: [8, 7, 9]
4. Implementation and Complexity
Now, let's look at the code. Since you are proficient in Python and it's a common language for interviews, we will focus on that implementation.
Sliding Window Maximum - Monotonic Queue - Leetcode 239
Let's return to the NeetCode video to see the full Python implementation and a final summary of the logic.
Watch from 05:31 to the end. The first part (until 12:02) is a detailed walkthrough of another example. The second part (from 12:02) presents and explains the Python code that implements our algorithm.
Here is the Python code from the takeuforward article for your reference. Notice how closely it maps to the algorithm steps we defined.
from collections import deque
from typing import List
class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
dq = deque()
result = []
for i in range(len(nums)):
# 1. Remove indices that are out of the current window's scope
if dq and dq[0] <= i - k:
dq.popleft()
# 2. Remove smaller elements from the back of the deque
while dq and nums[dq[-1]] < nums[i]:
dq.pop()
# 3. Add the current element's index to the deque
dq.append(i)
# 4. If the window is fully formed, append the max (at the front) to the result
if i >= k - 1:
result.append(nums[dq[0]])
return result
Complexity Analysis
- Time Complexity: O(n)
This is the most important takeaway. Although there is awhileloop inside theforloop, the analysis is amortized. Each element's index is pushed onto the deque exactly once and popped from the deque at most once throughout the entire process. Therefore, the total work is proportional to the number of elements,n, giving us a linear time complexity. - Space Complexity: O(k)
The deque stores indices. In the worst-case scenario (a strictly decreasing array), the deque will hold up tokindices at one time, one for each element in the window. Theresultarray does not count towards auxiliary space complexity in most interview contexts.
You can review a concise summary of the complexity in the final section of the takeuforward article (resource ID LINK).
Conclusion
Congratulations on mastering another advanced, high-ROI interview pattern! You have seen how to move from a naive O(n*k) solution to an optimal O(n) solution for the Sliding Window Maximum problem by using a deque to maintain a monotonic collection of candidates. This demonstrates a deep understanding of how data structures can be creatively applied to solve complex problems efficiently.
Key Takeaways:
- A deque provides O(1) insertion and deletion at both ends, making it perfect for managing a sliding window's contents.
- By maintaining a deque of indices whose corresponding values are in decreasing order, we ensure the maximum of the current window is always at the front, ready for O(1) access.
- The overall time complexity is O(n) because each element is processed (pushed and popped) a constant number of times over the entire traversal.
- This pattern is a powerful combination of the sliding window technique and the monotonic principle we learned in the last lesson.
We've now covered several key patterns for linear data structures. In our next lesson, we'll shift our focus to non-linear structures and begin our exploration of Binary Trees, starting with the fundamental traversal algorithms: pre-order, in-order, and post-order.