Problem
You are given an array of integers nums, there is a sliding window of size k which is moving from the very left of the array to the very right. You can only see the k numbers in the window. Each time the sliding window moves right by one position.
Return the max sliding window.
Sometimes k is also denoted as w.
Examples
Example 1
| |
Solution
There are a couple of solutions possible for this problem, lets look at them.
Method 1 - Brute Force
An O(n*w) bruteforce solution would be to do go through each of the element of the array and find minimum of w elements from the current position to the right.
Code
| |
Method 2 - Using Max Heap - but wrong approach ❌
This approach, even though looks legitimate will not work.
This will not work
If you closely observe the way we calculate the maximum in each w-sized subarray, you will notice that we’re doing repetitive work in the inner loop. Let’s understand it with an example:
| |
For the above example, we first calculate the maximum in the subarray {1, 2, 3}, then we move on to the next subarray {2, 3, 1} and calculate the maximum. Notice that, there is an overlap in both the subarrays:
| |
So we’re calculating the maximum in the overlapping part (2, 3) twice. We can utilize the sliding window technique to save us from re-calculating the maximum in the overlapping subarray and improve the run-time complexity.
In the sliding window technique, we consider each k-sized subarray as a sliding window. To calculate the maximum in a particular window (2, 3, 1), we utilize the calculation done for the previous window (1, 2, 3).
Since we want to utilize the calculation from the previous window, we need a way to store the calculation for the previous window. We’ll use a PriorityQueue to store the elements of the sliding window in decreasing order (maximum first).
Code
| |
| |
Method 3 - Using MaxHeap of Pair of Value and Index ✅
In method 2, we cannot figure out the way to remove values from heap, as it takes O(k) time.
What we can do is, to use a pair or [value, index] instead of just inserting the value to queue. Whenever we remove the value as max element from queue, we make sure that it is not a stale index.
Code
| |
Method 4 - Using balanced BST
Algorithm
- Construct a self-balancing binary search tree (BST) of size
wwith the firstwelements of the input array. - Iterate through the remaining elements (
n - wtimes):- Print the maximum element from the BST.
- Delete the current element (
arr[i]) from the BST. - Insert the element
kpositions ahead (arr[i+k]) into the BST.
Complexity
⏰ Time complexity:
O(n log w)🧺 Space complexity:
O(w)Initializing the BST (step 1) takes
O(w log w)due to insertions in a self-balancing BST.Finding the maximum, deleting, and inserting elements (steps 2.1, 2.2, 2.3) each have
O(log w)cost within the loop.Since the loop runs
n-w+1times, the total complexity isO(w log w + (n-w+1) * log w), which simplifies toO(n log w).
Method 5 - Using DP
- Leveraging Element Scope: We recognize that elements can contribute to windows
wpositions to their left and right. (This remains the same) - Divide and Conquer with Blocks: We exploit this by dividing the array of size
ninto blocks of sizew, so we getn/wblocks. (This remains the same) - Calculating Maximums:
- Within each block, we calculate the maximum elements seen so far (
leftMax) while traversing left to right. - We also calculate the maximum elements remaining (
rightMax) while traversing right to left.
- Within each block, we calculate the maximum elements seen so far (
- Non-Overlapping Windows: Importantly, windows separated by elements less than or equal to
wdon’t overlap. (Change “greater than or equal to” to “less than or equal to”) - Resetting at Block Boundaries: We reset
leftMaxandrightMaxvalues at the boundaries of each block. - Finding Maximum at Each Position: Finally, the maximum at each position
iin the current window (slidingMax[i]) is the maximum ofrightMax[i]and the correspondingleftMax[i + w - 1]shifted byw-1positions.
This approach achieves O(n) time complexity by efficiently calculating minimum elements within and across blocks.
Dry Run
For Example: A = [2, 1, 3, 4, 6, 3, 8, 9, 10, 12, 56], w=4
partition the array in blocks of size
w=4. The last block may have less then w.2, 1, 3, 4 | 6, 3, 8, 9 | 10, 12, 56|Traverse the list from start to end and calculate
maxSoFar. Reset min to 0 after each block (ofwelements).leftMax[] = 2, 2, 3, 4 | 6, 6, 8, 9 | 10, 12, 56Similarly calculate
maxInFutureby traversing from right to left.rightMax[] = 4, 4, 4, 4 | 9, 9, 9, 9 | 56, 56, 56now, min at each position i in current window,
slidingMax[i] = min (rightMax[i], leftMax[i+w-1])slidingMax = 4, 6, 6, 8, 9, 10, 12, ….
Code
| |
Complexity
- ⏰ Time complexity:
O(n) - 🧺 Space complexity:
O(n)
Method 6 - Using Monotonic Decreasing Deque (Save Even on space) 🏆
We can use deque datastructure.
Algorithm
- We iterate through the array, adding element indices (not values) to a deque.
- The deque maintains only the indices of the maximum elements within the current window of size
w. - To create the initial window, we add the first
welements and remove any smaller indices encountered, ensuring the deque holds only the maximum element indices. - As we slide the window by one element:
- We remove the index falling outside the new window.
- We again remove any smaller indices from the deque to maintain the maximum element focus.
- The first element in the deque corresponds to the maximum element in the current window (due to the descending order based on element values).
Code
| |
Complexity
- ⏰ Time complexity:
O(n + w) - 🧺 Space complexity:
O(w)
Dry Run
The maximum will always be the head of the queue.
| |