Loading problems...
TL;DR: The optimal solution utilizes a monotonic decreasing deque (double-ended queue) to store indices of potential maximum candidates, achieving linear time complexity by discarding elements that can no longer be the maximum.
In this problem, you are provided with an integer array nums and a sliding window of size k. As the window moves from the leftmost to the rightmost side of the array one position at a time, you must determine the maximum value present within the window at each step.
"LeetCode 239" is a classic algorithmic challenge that tests your ability to optimize range queries. While a naive approach is straightforward, the strict constraints require an solution, making "Sliding Window Maximum" a popular interview question for testing advanced data structure usage.
The most intuitive way to solve the Sliding Window Maximum problem is to iterate through every possible window position and scan all elements within that window to find the maximum.
Pseudo-code:
textInitialize an empty list result For i from 0 to length(nums) - k: current_max = -infinity For j from 0 to k - 1: current_max = max(current_max, nums[i + j]) Append current_max to result Return result
Complexity Analysis:
Why it fails:
The constraints state that nums.length can be up to . If is large, an approach will perform approximately operations, which far exceeds the typical limit of operations per second allowed by online judges. This results in a Time Limit Exceeded (TLE) error.
The transition from the brute force approach to the optimal solution relies on a key observation about "useless" elements.
Consider two indices and such that (index comes before ) and nums[i] <= nums[j]. If both indices are currently inside the sliding window, index can never be the maximum. Why?
nums[j] is greater than or equal to nums[i], so nums[i] is not the max right now.nums[i] will never become the maximum in the future while nums[j] is present.This implies that we can discard nums[i] immediately. We only need to store elements that have the potential to be the maximum. This logic leads us to maintain a data structure where elements are stored in strictly decreasing order.
Visual Description: Imagine the data structure as a queue of indices. As we scan the array from left to right:
index <= current_index - k), it is removed.
Let's trace the algorithm with nums = [1, 3, -1, -3, 5, 3, 6, 7] and k = 3.
[0] (val: 1).[1] (val: 3).[1, 2] (vals: 3, -1). Window [0, 2] complete. Max is nums[1] = 3.[1, 2, 3] (vals: 3, -1, -3). Max is nums[1] = 3.[4] (val: 5). Max is nums[4] = 5.[4, 5] (vals: 5, 3). Max is nums[4] = 5.[6] (val: 6). Max is nums[6] = 6.[7] (val: 7). Max is nums[7] = 7.Result: [3, 3, 5, 5, 6, 7]
The correctness relies on the invariant that the deque is strictly decreasing.
i - k). Thus, the front is always a valid index within the current window.cpp#include <vector> #include <deque> class Solution { public: std::vector<int> maxSlidingWindow(std::vector<int>& nums, int k) { std::deque<int> dq; // Stores indices std::vector<int> result; for (int i = 0; i < nums.size(); ++i) { // 1. Remove indices that are out of the current window if (!dq.empty() && dq.front() == i - k) { dq.pop_front(); } // 2. Remove indices whose corresponding values are less than nums[i] // These elements can never be the maximum while (!dq.empty() && nums[dq.back()] < nums[i]) { dq.pop_back(); } // 3. Add current index dq.push_back(i); // 4. Add the maximum (front of deque) to result // The first window ends at index k - 1 if (i >= k - 1) { result.push_back(nums[dq.front()]); } } return result; } };
javaimport java.util.ArrayDeque; import java.util.Deque; class Solution { public int[] maxSlidingWindow(int[] nums, int k) { if (nums == null || k <= 0) { return new int[0]; } int n = nums.length; int[] result = new int[n - k + 1]; int resultIdx = 0; // Deque stores indices Deque<Integer> dq = new ArrayDeque<>(); for (int i = 0; i < n; i++) { // 1. Remove indices out of the current window if (!dq.isEmpty() && dq.peekFirst() == i - k) { dq.pollFirst(); } // 2. Remove elements smaller than current from the back // Maintain decreasing order while (!dq.isEmpty() && nums[dq.peekLast()] < nums[i]) { dq.pollLast(); } // 3. Add current index dq.offerLast(i); // 4. Add max to result once the first window is formed if (i >= k - 1) { result[resultIdx++] = nums[dq.peekFirst()]; } } return result; } }
pythonfrom collections import deque from typing import List class Solution: def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]: dq = deque() # Stores indices result = [] for i in range(len(nums)): # 1. Remove index if it's out of the current window if dq and dq[0] == i - k: dq.popleft() # 2. Remove indices from back if their values are smaller than current while dq and nums[dq[-1]] < nums[i]: dq.pop() # 3. Add current index dq.append(i) # 4. Append max to result (only after first window is complete) if i >= k - 1: result.append(nums[dq[0]]) return result
while loop inside the for loop, each element is added to the deque exactly once and removed from the deque at most once. The amortized cost per element is constant.The Sliding Window - Monotonic Queue pattern is highly reusable for problems involving range extrema or constraints.
k positions during Dynamic Programming.Why does my solution get Time Limit Exceeded (TLE)?
dq.front() in .Why is my output incorrect for large windows?
index == i - kWhy is my Priority Queue (Heap) solution slow?
Why am I getting an Index Out of Bounds error?
result or add to result before the first window is fully formed.i >= k - 1.