Sliding Window Maximum Using Deque in DSA Rust - Time & Space Complexity
We want to understand how fast the sliding window maximum algorithm runs when using a deque.
Specifically, how the number of steps grows as the input array gets bigger.
Analyze the time complexity of the following code snippet.
fn max_sliding_window(nums: &[i32], k: usize) -> Vec {
use std::collections::VecDeque;
let mut dq: VecDeque = VecDeque::new();
let mut result = Vec::new();
for i in 0..nums.len() {
while let Some(&back) = dq.back() {
if nums[back] < nums[i] { dq.pop_back(); } else { break; }
}
dq.push_back(i);
if i >= k - 1 {
while dq.front().unwrap() <= i - k { dq.pop_front(); }
result.push(nums[*dq.front().unwrap()]);
}
}
result
}
This code finds the maximum value in every window of size k as it slides over the array nums.
Identify the loops, recursion, array traversals that repeat.
- Primary operation: A single loop over all elements of
nums. - How many times: Exactly once for each element (n times).
- Inside the loop, elements are added and removed from the deque at most once each.
As the input size n grows, each element is processed a limited number of times.
| Input Size (n) | Approx. Operations |
|---|---|
| 10 | About 20 operations (each element pushed and popped once) |
| 100 | About 200 operations |
| 1000 | About 2000 operations |
Pattern observation: The operations grow roughly linearly with input size.
Time Complexity: O(n)
This means the algorithm runs in time proportional to the number of elements, processing each element a constant number of times.
[X] Wrong: "Because there is a nested while loop inside the for loop, the time complexity must be O(n²)."
[OK] Correct: Each element is added and removed from the deque only once, so the inner loop does not run n times for each outer loop iteration.
Understanding this algorithm shows you can use data structures smartly to improve performance from a naive O(n*k) to an optimal O(n).
This skill is valuable for solving many real-world problems efficiently.
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: First window
Window [1, 3, -1] contains elements at indices 0, 1, 2.Step 2: Maximum
max(1, 3, -1) = 3.Step 3: Deque state
After processing first window: deque = [1] (index of 3, since 3 is largest and -1 is removed because 3 > -1 and 3 came before -1).Final Answer:
3 -> Option CQuick Check:
max(1,3,-1)=3. [OK]
- Returning the last element -1
- Returning the first element 1
- Not recognizing 3 as the maximum
Solution
Step 1: Deque content
The deque stores INDICES (not values), arranged so that nums[deque.front()] is always the maximum of the current window.Step 2: Decreasing order
Elements are kept in decreasing order of their values - smaller elements behind a larger one are useless (they can never be the maximum while the larger element is in the window).Final Answer:
Indices of elements in decreasing value order. -> Option BQuick Check:
Deque = indices in decreasing value order. Front = current window max. [OK]
- Thinking deque stores values not indices
- Thinking all window elements are stored
- Confusing with a max-heap approach
Solution
Step 1: Each element pushed once
Each element is pushed to the deque exactly once.Step 2: Each element popped at most once
Each element is popped from either front or back at most once. Total push+pop operations: 2n = O(n).Step 3: Overall
n iterations Ć O(1) amortized per iteration = O(n).Final Answer:
O(n) - each element pushed and popped at most once. -> Option CQuick Check:
Each element: 1 push + at most 1 pop = O(n) total. [OK]
- Saying O(nĆk) by not accounting for amortized analysis
- Saying O(n log n) confusing with heap-based approach
- Not knowing each element is pushed/popped at most once total
Solution
Step 1: Why pop from back
If nums[deque.back()] <= nums[i], the back element can never be the window maximum while nums[i] is in the window. nums[i] is larger AND will leave the window later.Step 2: Keep decreasing order
Pop all elements from back where nums[back] <= nums[i], then push i. This maintains the monotonic decreasing invariant.Final Answer:
Pop back when nums[deque.back()] <= nums[i]. -> Option BQuick Check:
Back pop: current element >= back element ā back is useless, remove it. [OK]
- Confusing front pop (out-of-window check) with back pop (useless element removal)
- Popping only when strictly less than, missing equals case
- Thinking deque size triggers back pop
Solution
Step 1: Window boundary
The current window is [i-k+1, i]. The front of deque holds the maximum's index. If that index <= i-k, it's outside the window.Step 2: Pop expired maximum
Pop front when deque.front() <= i - k. The front is no longer in the current window - the next front becomes the new maximum.Final Answer:
Pop front when front index <= i - k (out of window). -> Option BQuick Check:
Front pop: index out of window (front <= i-k). Back pop: value useless (back <= current). [OK]
- Confusing front pop condition with back pop condition
- Using < i-k instead of <= i-k (off by one)
- Thinking front pop is based on value comparison
Ready to prove it?
Pass the test and this pattern moves unknown
