Sliding Window Maximum Using Deque in DSA C++ - Time & Space Complexity
We want to understand how fast we can find the maximum value in every sliding window of a list.
How does the time needed grow as the list gets bigger?
Analyze the time complexity of the following code snippet.
#include <deque>
#include <vector>
using namespace std;
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
deque<int> dq;
vector<int> result;
for (int i = 0; i < nums.size(); i++) {
while (!dq.empty() && dq.front() <= i - k) dq.pop_front();
while (!dq.empty() && nums[dq.back()] < nums[i]) dq.pop_back();
dq.push_back(i);
if (i >= k - 1) result.push_back(nums[dq.front()]);
}
return result;
}
This code finds the maximum value in each sliding window of size k using a double-ended queue.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: A single loop over the input array from start to end.
- How many times: Exactly once for each element (n times), with some extra pops from the deque inside the loop.
Each element is added and removed from the deque at most once, so the total operations grow roughly in a straight line with input size.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 operations (adding and removing elements) |
| 100 | About 200 operations |
| 1000 | About 2000 operations |
Pattern observation: The operations increase roughly in direct proportion to the input size.
Time Complexity: O(n)
This means the time to find all sliding window maximums grows linearly with the size of the input list.
[X] Wrong: "Because there are nested while loops, the time complexity must be O(n²)."
[OK] Correct: Each element is pushed and popped at most once, so the total work inside the loops is still proportional to n, not n squared.
Understanding this technique shows you can optimize problems by cleverly using data structures to avoid repeated work.
"What if we changed the deque to a simple list and removed elements from the front by shifting? How would the time complexity change?"
Practice
Solution
Step 1: Understand the role of deque
The deque stores indices of elements in the current window, maintaining them in decreasing order of their values.Step 2: How deque helps find maximum
The front of the deque always holds the index of the maximum element for the current window, allowing O(1) access.Final Answer:
To store indices of elements in a way that helps find the maximum in O(1) time per window -> Option AQuick Check:
Deque stores indices for max retrieval [OK]
- Thinking deque stores actual values instead of indices
- Assuming deque sorts the entire array
- Believing deque reverses the array
Solution
Step 1: Identify deque operation for removing from back
pop_back() removes the last element from the deque, which is used to discard smaller elements behind the current element.Step 2: Why pop_back() is used here
We remove elements from the back that are smaller than the current element to maintain decreasing order in the deque.Final Answer:
pop_back() -> Option CQuick Check:
pop_back() removes smaller elements from back [OK]
- Using pop_front() instead of pop_back() to remove from back
- Confusing push operations with pop operations
- Removing elements from front incorrectly
[2, 1, 3, 4, 6, 3, 8, 9, 10, 12, 56] with window size 4?Solution
Step 1: Slide window and find max for each window
Windows and max values: [2,1,3,4] -> 4, [1,3,4,6] -> 6, [3,4,6,3] -> 6, [4,6,3,8] -> 8, [6,3,8,9] -> 9, [3,8,9,10] -> 10, [8,9,10,12] -> 12, [9,10,12,56] -> 56Step 2: Collect all max values in order
The max values for each window are [4, 6, 6, 8, 9, 10, 12, 56]Final Answer:
[4, 6, 6, 8, 9, 10, 12, 56] -> Option DQuick Check:
Sliding window max matches [4, 6, 6, 8, 9, 10, 12, 56] [OK]
- Confusing window size or missing last windows
- Incorrectly calculating max for each window
- Off-by-one errors in output length
for (int i = 0; i < n; i++) {
while (!dq.empty() && arr[dq.back()] <= arr[i]) dq.pop_back();
dq.push_back(i);
if (dq.front() == i - k) dq.pop_front();
if (i >= k - 1) result.push_back(arr[dq.front()]);
}Solution
Step 1: Understand when to remove front index
The front index should be removed if it is out of the current window. The window covers indices [i-k+1, i]. So indices less than i-k+1 are out.Step 2: Correct condition for popping front
The condition should be dq.front() <= i - k to remove indices outside the window.Final Answer:
The condition to pop front should be dq.front() <= i - k -> Option BQuick Check:
Pop front if index outside window [OK]
- Using equality instead of less than for popping front
- Off-by-one errors in window boundary
- Not removing outdated indices from deque
[1, 3, -1, -3, 5, 3, 6, 7] and window size 3, how would you modify the deque approach to also return the minimum in each sliding window along with the maximum?Solution
Step 1: Understand separate tracking for max and min
To get both max and min efficiently, maintain two deques: one keeps indices of max candidates in decreasing order, the other keeps indices of min candidates in increasing order.Step 2: How two deques work together
For each window, the front of max deque gives max, front of min deque gives min. Both deques are updated similarly but with opposite ordering.Final Answer:
Use two deques: one for max (decreasing) and one for min (increasing) values -> Option AQuick Check:
Two deques track max and min separately [OK]
- Trying to store both max and min in one deque
- Sorting each window (inefficient)
- Using wrong data structures for min tracking
Ready to prove it?
Pass the test and this pattern moves unknown
