Sliding Window
From CPH:
A sliding window is a constant-size subarray that moves from left to right through the array.
For each position of the window, we wish to compute some information.
Focus Problem – try your best to solve this problem before continuing!
Implementation
The most straightforward way to do this is to maintain a sorted set of integers containing the integers inside the window. If the window currently spans the range , we observe that sliding the range forward to removes and adds to the window. We can support these two operations and query for the minimum / maximum in the set in .
Time Complexity:
C++
vector<int> maxSlidingWindow(vector<int> &nums, int k) {multiset<int> s;vector<int> ret;for (int i = 0; i < k; i++) { s.insert(nums[i]); }for (int i = k; i < nums.size(); i++) {ret.push_back(*s.rbegin());s.erase(s.find(nums[i - k]));s.insert(nums[i]);}ret.push_back(*s.rbegin());return ret;}
Java
static TreeMap<Integer, Integer> multiset = new TreeMap<Integer, Integer>();static void add(int x) {if (multiset.containsKey(x)) {multiset.put(x, multiset.get(x) + 1);} else {multiset.put(x, 1);}}static void remove(int x) {multiset.put(x, multiset.get(x) - 1);
Python
from sortedcontainers import SortedListclass Solution:def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:s = SortedList(nums[:k])ret = []for i in range(k, len(nums)):ret.append(s[-1])s.remove(nums[i - k])s.add(nums[i])ret.append(s[-1])return ret
Problems
| Status | Source | Problem Name | Difficulty | Tags | ||
|---|---|---|---|---|---|---|
| CSES | Normal | Show TagsPrefix Sums, Sliding Window | ||||
| CSES | Normal | Show TagsSet, Sliding Window | ||||
| CSES | Hard | Show TagsSet, Sliding Window | ||||
With Two Pointers
In general, it isn't required for the subarray to have constant size as long as both the left and right endpoints of the subarray only move to the right.
Focus Problem – try your best to solve this problem before continuing!
Solution
We keep a pointer for the left boundary of the window and expand the right boundary until we find a song already in our subarray. Checking songs can be done with a set.
Then, we erase the left boundary from the set and move the left pointer up by one. We do this until the right boundary is able to expand. The left boundary is removed until the song immediately after the right boundary isn't in our current window's set. The left boundary is removed to provide more flexibility for expanding the right boundary.
The answer is the largest distance between the left and right pointers.
C++
#include <bits/stdc++.h>using namespace std;int n;set<int> s;int a[200000], ans;int main() {int r = -1;cin >> n;
Java
public class Playlist {public static void main(String[] args) throws Exception {BufferedReader br = new BufferedReader(new InputStreamReader(System.in));int n = Integer.parseInt(br.readLine());StringTokenizer st = new StringTokenizer(br.readLine());int a[] = new int[n];for (int i = 0; i < n; i++) a[i] = Integer.parseInt(st.nextToken());int r = -1;HashSet<Integer> s = new HashSet<Integer>();int ans = 0;
Python
n = int(input().strip())nums = [int(v) for v in input().split(" ")]ans = -float("inf")left, right = 0, 0unique_songs = set()while right < n:# Notice that all the songs in unique_songs are unique in each iteration.# We keep this property by shrinking the window before inserting nums[right].
Problems
| Status | Source | Problem Name | Difficulty | Tags | ||
|---|---|---|---|---|---|---|
| CF | Easy | Show Tags2P, Binary Search | ||||
| Gold | Easy | Show TagsSet, Sliding Window | ||||
| AC | Easy | Show TagsSliding Window | ||||
| CSES | Easy | Show Tags2P, Sliding Window | ||||
| APIO | Normal | Show TagsMedian, Sliding Window | ||||
| Gold | Normal | Show TagsSliding Window | ||||
| Platinum | Normal | Show TagsSliding Window | ||||
| APIO | Hard | Show TagsDP, Sliding Window | ||||
| IOI | Hard | Show TagsBinary Search, DP, Sliding Window | ||||
| IOI | Hard | Show TagsDP, Sliding Window | ||||
Sliding Window Maximum in
Focus Problem – try your best to solve this problem before continuing!
Resources
| Resources | |||||
|---|---|---|---|---|---|
| cp-algo | multiple ways to solve this | ||||
Method 1 - Deque
| Resources | |||||
|---|---|---|---|---|---|
| CPH | |||||
Monotonoic Queue
A monotonic queue is one that is always increasing or decreasing. It is implemented by ensuring the newest elements are larger or smaller than the previous elements. If not, the previous elements are popped until the condition is met.
C++
vector<int> nums{1, 2, 3, 5, 4};deque<int> increasing;deque<int> decreasing;for (int e : nums) {while (!increasing.empty() && increasing.back() > e) { increasing.pop_back(); }increasing.push_back(e);}// 1 2 3 4
Java
int[] nums = {1, 2, 3, 5, 4};ArrayDeque<Integer> increasing = new ArrayDeque<Integer>();ArrayDeque<Integer> decreasing = new ArrayDeque<Integer>();for (int e : nums) {while (!increasing.isEmpty() && increasing.peekLast() > e) {increasing.pollLast();}increasing.addLast(e);}
Python
nums = [1, 2, 3, 5, 4]increasing = collections.deque()decreasing = collections.deque()for e in nums:while increasing and increasing[-1] > e:increasing.pop()increasing.append(e)print(increasing) # deque([1, 2, 3, 4])
We keep a decreasing monotonic queue for finding a sliding window maximum. An increasing monotonic queue would only work for finding the sliding window minimum, as it removes large numbers and keeps small numbers.
A decreasing monotonic queue removes elements that are smaller than our current element and only keeps elements larger than our current element in order to maintain monotonicity. In this case, a decreasing monotonic queue works well as any elements smaller than our current element will serve no use to us finding a sliding window maximum.
A monotonic queue is similar to a monotonic stack, but due to its deque implementation, a monotonic queue has access to both the front and end, making it efficient for sliding window problems. A monotonic stack is useful for nearest greater/smaller element.
Deque - Storing Indices
This is method 2 from cp-algo.
The solution is a monotonic queue with the added window size constraints. We print the first element (which is the largest, because the queue is decreasing), and then pop it if it is the boundary of our current window. We pop it because the first element is also the oldest due to the FIFO nature of queues, meaning it must be removed to shift the window right. Then, we remove any numbers that are smaller than our current number to maintain monotonicity.
C++
vector<int> maxSlidingWindow(vector<int> &nums, int k) {vector<int> ret;deque<int> d;for (int i = 0; i < nums.size(); i++) {if (!d.empty() && d.front() <= i - k) { d.pop_front(); }while (!d.empty() && nums[d.back()] < nums[i]) { d.pop_back(); }d.push_back(i);
Java
public int[] maxSlidingWindow(int[] nums, int k) {int[] ret = new int[nums.length - k + 1];ArrayDeque<Integer> d = new ArrayDeque<Integer>();for (int i = 0; i < nums.length; i++) {if (!d.isEmpty() && d.peekFirst() <= i - k) { d.pollFirst(); }while (!d.isEmpty() && nums[d.peekLast()] < nums[i]) { d.pollLast(); }d.addLast(i);
Python
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:d = collections.deque()ret = []for i, num in enumerate(nums):if d and d[0] <= i - k:d.popleft()while d and nums[d[-1]] < num:d.pop()
Deque - Storing Values
Method 1 from CP Algorithms is a similar approach but stores the value itself rather than indices.
Inserting an element is the same process, but deleting an element is different. Because no indices are given this time, we check if the front of the queue is the same as the left boundary of the sliding window. If it is, then we remove it to ensure our window constraints are met.
C++
vector<int> maxSlidingWindow(vector<int> &nums, int k) {vector<int> res;deque<int> d;for (int i = 0; i < nums.size(); i++) {while (!d.empty() && d.back() < nums[i]) { d.pop_back(); }d.push_back(nums[i]);if (i >= k - 1) { res.push_back(d.front()); }
Java
public int[] maxSlidingWindow(int[] nums, int k) {
int[] ret = new int[nums.length - k + 1];
ArrayDeque<Integer> d = new ArrayDeque<Integer>();
for (int i = 0; i < nums.length; i++) {
while (!d.isEmpty() && d.peekLast() < nums[i]) {
d.pollLast();
}
d.addLast(nums[i]);
if (i >= k - 1) { ret[i - k + 1] = d.peekFirst(); }
if (i >= k - 1 && !d.isEmpty() && d.peekFirst() == nums[i - k + 1]) {
d.pollFirst();
}
}
return ret;
}Python
def maxSlidingWindow(self, nums: List[int], k: int) -> List[int]:d = collections.deque()ret = []for i, num in enumerate(nums):while d and d[-1] < num:d.pop()d.append(num)
Method 2 - Two Stacks
Method 3 from cp-algo. Not as common but nice to know!
We use two stacks and to simulate our maximum queue. Every time we add an element to it, we push the element itself and the maximum in stack after adding this element to . To pop out the front element from our queue, we just pop the top element from . Since elements in are stored in the order of how we added them, i.e. the last added element is at the top of , we just have to pop all of them out and push them into the stack when is empty. After that, these elements in will be in reversed order, i.e. the first added element will be at the top of , and we can pop them out as from normal stacks to simulate the dequeue operation of our queue. To find the maximum among all elements in our queue, we just have to return the maximum of both stacks, which is stored in the top element when we added it.
Then, we can solve the problem by removing the first element and adding a new element to the queue to simulate our sliding window. As each operation of our queue takes time, and we add each of elements once, we get a time complexity of .
C++
struct MaxQueue {/*** For each pair<int, int> e, e.first is the value of the element and* e.second is the maximum among all elements in that stack under element e.*/stack<pair<int, int>> s1, s2;/*** Get the maximum element in the MaxQueue.* It is the maximum of both stacks which are stored in s.top().second.
Java
class Solution {public int[] maxSlidingWindow(int[] nums, int k) {MaxQueue q = new MaxQueue();int n = nums.length;int[] maxVals = new int[n - k + 1];// Fill the queue with elements from the first windowfor (int i = 0; i < k; i++) { q.enqueue(nums[i]); }maxVals[0] = q.query();
Python
class MaxQueue:def __init__(self):"""For each pair (val, max_val) in the stacks:- val: The value of the element.- max_val: The maximum among all elements in that stack under element val."""self.s1 = []self.s2 = []
Problems
| Status | Source | Problem Name | Difficulty | Tags | ||
|---|---|---|---|---|---|---|
| COCI | Normal | Show TagsSliding Window | ||||
| YS | Hard | Show TagsSliding Window | ||||
| Baltic OI | Hard | Show TagsSliding Window | ||||
| POI | Hard | Show TagsSliding Window | ||||
| CC | Very Hard | Show TagsDP, Sliding Window | ||||
Module Progress:
Join the USACO Forum!
Stuck on a problem, or don't understand a module? Join the USACO Forum and get help from other competitive programmers!