Hello! Welcome to your next lesson.
In our previous session, you learned how to implement a deque using a doubly linked list and saw how the dummy node technique can simplify edge-case handling. This gave you a solid understanding of how a deque provides O(1) insertions and deletions at both ends.
Today, we'll apply that knowledge to solve a classic problem that frequently appears in technical interviews: Sliding Window Maximum. This lesson will demonstrate a powerful pattern where the deque isn't just a simple container but an intelligent tool for maintaining order and efficiently tracking candidates for a maximum value. Your goal is to learn how to solve this problem in linear time using a deque.
The Problem: Sliding Window Maximum
Imagine you have an array of numbers and a window of a fixed size k. This window slides from the very left of the array to the very right, one position at a time. For each position of the window, you need to find the maximum element inside it.
Let's take an example: nums = [1, 3, -1, -3, 5, 3, 6, 7] and k = 3.

The process would look like this:
[1, 3, -1]-3, 5, 3, 6, 7 (Max is 3)- 1,
[3, -1, -3]5, 3, 6, 7 (Max is 3) - 1, 3,
[-1, -3, 5]3, 6, 7 (Max is 5) - ...and so on.
The final output would be [3, 3, 5, 5, 6, 7].
1. The Brute-Force Approach
The most straightforward solution is to simply follow the problem description. You can iterate through each possible starting position of the window and then loop through the k elements within that window to find the maximum.
This approach involves a nested loop structure, which gives it a time complexity of O(N * k), where N is the number of elements in the array. While this works for small inputs, it's too slow for the constraints typical in competitive programming and interviews (e.g., N up to 10^5).
To see this explained, watch the beginning of the following video.
Sliding Window Maximum | Leetcode
This video from 'take U forward' first explains the problem and the brute-force O(N*K) solution.
Watch from the beginning until timestamp 02:36 to understand the problem statement and the naive approach.
2. Towards an Optimal Solution: Why Not a Heap?
Your first instinct to optimize might be to use a data structure designed for finding maximums quickly, like a max-heap (PriorityQueue in Java). You could maintain a heap of the k elements in the window. The maximum would always be at the root, accessible in O(1).
However, when the window slides, you must remove the element that falls out of the window's left boundary. Removing an arbitrary element from a heap is an O(k) operation, which brings us back to an O(N*k) total complexity. A slightly more clever approach involves not removing elements immediately, but only checking if the root of the heap is outdated. This improves the complexity to O(N log k). While better, we can still do one better!
The key insight is that we are doing redundant work. We don't need to keep all k elements to know the maximum. We only need to keep track of the potential candidates for the maximum.
3. The Optimal Solution: The Monotonic Deque
This is where the deque shines. We will use a deque to store the indices of elements in the current window. The crucial idea is to maintain a monotonically decreasing sequence of elements in the deque. This means the element at the front of the deque is always the largest, the next one is the second largest of the remaining, and so on.
By storing indices instead of values, we can do two things:
- Access the element's value using
nums[index]. - Know if an element is out of the current window's bounds.
To maintain this "monotonic deque," we follow three simple rules as we iterate through the array from left to right with an index i:
-
Clean the Front: Before processing
nums[i], look at the index at the front of the deque. If this index is outside the current window (i.e.,deque.front() <= i - k), it's no longer relevant, so remove it from the front (pollFirst()). -
Clean the Back: Now, look at the element
nums[i]. We need to add its indexito the deque, but first, we must maintain the decreasing property. While the deque is not empty and the element corresponding to the index at the back is smaller thannums[i], remove that index from the back (pollLast()). Why? Because ifnums[i]is larger, the smaller element at the back can never be a maximum in any future window that includesnums[i]. It has become useless. -
Add to Back: Add the current index
ito the back of the deque (offerLast()).
After these steps, the index at the front of the deque always corresponds to the maximum element in the current window. Once our window is fully formed (i >= k - 1), we can add nums[deque.peekFirst()] to our result list.
The following video provides an excellent animated walkthrough of this logic.
Sliding Window Maximum | Leetcode
Let's watch the core logic explained. Pay close attention to how elements are removed from both ends of the deque to maintain the decreasing order.
Watch the segment from 03:23 to 12:10. This is the most important part of the lesson and explains the entire algorithm with a step-by-step example.
Here is a static image that also walks through the process, which you can use as a reference.

Test your understanding!
Given the array nums = [5, 3, 8, 2, 6] and k = 3, what is the state of the deque (containing indices) after processing the element 8 at index 2?
- i = 0, nums[0] = 5: Deque is empty. Add index 0. Deque:
[0] - i = 1, nums[1] = 3:
nums[0](5) >nums[1](3). Add index 1. Deque:[0, 1] - i = 2, nums[2] = 8: What happens now?
Show answer
When processing nums[2] = 8:
- Clean Front: The window is
[0, 2]. No indices are out of bounds. - Clean Back:
- Check the last index in the deque:
1.nums[1](3) <nums[2](8). Pop1. Deque is now[0]. - Check the new last index:
0.nums[0](5) <nums[2](8). Pop0. Deque is now empty.
- Check the last index in the deque:
- Add to Back: Add the current index
2.
The final state of the deque is [2]. The maximum for the first window [5, 3, 8] is nums[deque.peekFirst()] which is nums[2], or 8.
4. Implementation and Complexity Analysis
Now, let's put this into code and analyze its efficiency.
Sliding Window Maximum - Tutorial
This article from takeuforward.org provides a clean Java implementation of the optimal deque-based approach. It's a great reference to see the logic translated into code.
Focus on the 'Optimal Approach' section. First read the algorithm description, then carefully examine the Java code provided. Finally, read the 'Complexity Analysis' for the optimal solution.
Time Complexity: O(N)
You might think the nested while loop inside the for loop makes the complexity worse than O(N). However, we can use amortized analysis. Each element's index is pushed onto the deque exactly once. It is also popped from the deque at most once. Therefore, over the entire algorithm, every element results in a constant number of deque operations. This leads to a total time complexity of O(N).
Space Complexity: O(k)
In the worst case (a strictly decreasing array), the deque will hold k indices at a time. Therefore, the space complexity is O(k).
For a deeper dive into the concept, sometimes called a "Monotonic Queue," and its implementation as a class (a very clean software engineering practice), you can explore this resource.
Monotonic Queue to Solve Sliding Window Problems
This article from labuladong.online formalizes the data structure we've been using as a 'MonotonicQueue' and implements it as a distinct class in Java. This is an excellent way to structure your code for clarity and reusability.
Read '2. Implementing the Monotonic Queue Data Structure' to see how the push, max, and pop methods are built. Then look at the complete solution in the subsequent section to see how it's used. Finally, read the section on complexity, which provides another excellent explanation of the amortized O(N) time complexity.
Conclusion
Congratulations! You've just learned how to solve a challenging and common interview problem using a deque in the most optimal way. This technique of using a deque to maintain a monotonic (either increasing or decreasing) sequence of candidates is a powerful pattern that can be applied to other problems as well.
Key Takeaways:
- The brute-force solution for Sliding Window Maximum is O(N*k).
- A heap-based solution improves this to O(N log k) but is still not optimal.
- The optimal solution uses a deque to maintain a list of indices whose corresponding values are monotonically decreasing.
- This "monotonic deque" allows you to find the maximum in each window in amortized O(1) time, leading to an overall O(N) time complexity and O(k) space complexity.
Preview of the next lesson:
We'll now switch gears back to stacks. In the next lesson, you will learn how to solve another set of classic computer science problems: converting an infix expression to postfix (Reverse Polish Notation) and evaluating it using a stack. This is a fundamental concept in compiler design and expression parsing.