You are given an array of integers nums and an integer k. There is a sliding window of size k that starts at the left edge of the array. The window slides one position to the right until it reaches the right edge of the array.
Return a list that contains the maximum element in the window at each step.
Example 1:
Input: nums = [1,2,1,0,4,2,6], k = 3
Output: [2,2,4,4,6]
Explanation:
Window position Max
--------------- -----
[1 2 1] 0 4 2 6 2
1 [2 1 0] 4 2 6 2
1 2 [1 0 4] 2 6 4
1 2 1 [0 4 2] 6 4
1 2 1 0 [4 2 6] 6Constraints:
1 <= nums.length <= 1000-10,000 <= nums[i] <= 10,0001 <= k <= nums.length
You should aim for a solution as good or better than O(nlogn) time and O(n) space, where n is the size of the input array.
A brute force solution would involve iterating through each window of size k and finding the maximum element within the window by iterating through it. This would be an O(n * k) solution. Can you think of a better way? Maybe think of a data structure that tells the current maximum element of the window in O(1) time.
A heap is the best data structure to use when dealing with maximum or minimum values and it takes O(1) time to get the max or min value. Here, we use a max-heap. But what should we do if the current maximum element is no longer part of the window? Can you think of a different way of adding values to the max-heap?
We process each window by adding elements to the heap along with their indices to track whether the maximum value is still within the current window. As we move from one window to the next, an element may go out of the window but still remain in the max-heap. Is there a way to handle this situation efficiently?
We can ignore those elements that are no longer part of the current window, except when the maximum value is outside the window. In that case, we remove elements from the max-heap until the maximum value belongs to the current window. Why? Because those elements will be eventually removed when the maximum element goes out of the window.
For every possible window of size k, we simply look at all k elements and pick the maximum.
We slide the window one step at a time, and each time we scan all elements inside it to find the max.
This method is very easy to understand but slow, because we repeatedly re-scan many of the same elements.
output to store the maximum of each window.i from 0 to len(nums) - k:maxi to nums[i].i to i + k - 1 and update maxi.maxi to output.output after checking all windows.Where is the length of the array and is the size of the window.
The brute-force solution recomputes the maximum for every window by scanning all k elements each time, which is slow.
A Segment Tree helps us answer “what is the maximum in this range?” much faster after some preprocessing.
Think of the segment tree as a special structure built on top of the array where:
Once we build this tree:
[L, R] (our sliding window) in O(log n) time.So the process is:
build once → query many times efficiently.
Build the Segment Tree
tree big enough to store a full binary tree.tree).tree[1] (the root) holds the maximum of the entire array.Range Maximum Query (Segment Tree query(l, r)):
l and r to point to the leaves representing those indices.l <= r in the tree:l is a right child, consider tree[l] in the answer and move l to the next segment.r is a left child, consider tree[r] in the answer and move r to the previous segment.l and r up one level by dividing by 2.Sliding Window using the Segment Tree
i (from 0 to n - k):[i, i + k - 1].output list.Return the output list containing the maximum for each sliding window.
class SegmentTree:
def __init__(self, N, A):
self.n = N
while (self.n & (self.n - 1)) != 0:
self.n += 1
self.build(N, A)
def build(self, N, A):
self.tree = [float('-inf')] * (2 * self.n)
for i in range(N):
self.tree[self.n + i] = A[i]
for i in range(self.n - 1, 0, -1):
self.tree[i] = max(self.tree[i << 1], self.tree[i << 1 | 1])
def query(self, l, r):
res = float('-inf')
l += self.n
r += self.n + 1
while l < r:
if l & 1:
res = max(res, self.tree[l])
l += 1
if r & 1:
r -= 1
res = max(res, self.tree[r])
l >>= 1
r >>= 1
return res
class Solution:
def maxSlidingWindow(self, nums, k):
n = len(nums)
segTree = SegmentTree(n, nums)
output = []
for i in range(n - k + 1):
output.append(segTree.query(i, i + k - 1))
return outputWe want to quickly get the maximum value inside a sliding window that moves across the array.
A max-heap is perfect for this because it always lets us access the largest element instantly.
As we slide the window:
This way, we efficiently maintain the maximum even as the window moves.
(value, index) for all elements we encounter.k:index is outside the current window.result list.class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
heap = []
output = []
for i in range(len(nums)):
heapq.heappush(heap, (-nums[i], i))
if i >= k - 1:
while heap[0][1] <= i - k:
heapq.heappop(heap)
output.append(-heap[0][0])
return outputInstead of recalculating the maximum for each sliding window, we can preprocess the array so that every window’s maximum can be answered in O(1) time.
We divide the array into blocks of size k.
Within each block, we build:
For any sliding window:
rightMax[i]leftMax[i + k - 1]The true window maximum is the larger of those two.
This lets us compute each window's maximum instantly.
k.leftMax array:rightMax array:k:max(rightMax[window_start], leftMax[window_end]).class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
n = len(nums)
leftMax = [0] * n
rightMax = [0] * n
leftMax[0] = nums[0]
rightMax[n - 1] = nums[n - 1]
for i in range(1, n):
if i % k == 0:
leftMax[i] = nums[i]
else:
leftMax[i] = max(leftMax[i - 1], nums[i])
if (n - 1 - i) % k == 0:
rightMax[n - 1 - i] = nums[n - 1 - i]
else:
rightMax[n - 1 - i] = max(rightMax[n - i], nums[n - 1 - i])
output = [0] * (n - k + 1)
for i in range(n - k + 1):
output[i] = max(leftMax[i + k - 1], rightMax[i])
return outputA deque helps us efficiently track the maximum inside the sliding window.
The key idea is to keep the deque storing indices of elements in decreasing order of their values.
This guarantees that:
By maintaining this structure, each element is added and removed at most once, giving an optimal solution.
right pointer:left pointer passes the front index, remove it (it's outside the window).k, the front of the deque represents the maximum — add it to the output.class Solution:
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:
output = []
q = deque() # index
l = r = 0
while r < len(nums):
while q and nums[q[-1]] < nums[r]:
q.pop()
q.append(r)
if l > q[0]:
q.popleft()
if (r + 1) >= k:
output.append(nums[q[0]])
l += 1
r += 1
return outputWhen using the deque approach, a common mistake is storing the actual values rather than their indices. Storing values makes it impossible to determine when an element has left the window, since you cannot compare positions. Always store indices in the deque and use nums[index] to access values.
Many solutions fail by using wrong conditions for when to start recording results or when to shrink the window. The first valid window exists when right >= k - 1 (or equivalently right + 1 >= k). Off-by-one errors here cause either missing the first window's maximum or including results before the window is fully formed.
The deque must maintain indices in decreasing order of their corresponding values. A common bug is using <= instead of < when comparing values before popping, or forgetting to pop elements altogether. This results in the front of the deque not representing the actual maximum. Always pop from the back while nums[deque.back()] < nums[current].
The number of windows is n - k + 1, not n or n - k. Allocating an output array of the wrong size leads to index out of bounds errors or missing results. This is especially problematic when k equals the array length, where there should be exactly one result.
When using segment trees, a frequent mistake is confusing inclusive versus exclusive range boundaries. The query range [i, i + k - 1] is inclusive on both ends for a window starting at index i. Mixing up these boundaries causes queries to include elements outside the window or miss elements at the boundaries.