Sliding Window
Maintain a window of elements in an array or string, expanding and contracting it to find optimal subarrays or substrings in O(n) time.
Learn & Reference
Understanding the Pattern
Sliding Window Pattern
The sliding window pattern transforms brute-force O(n²) nested-loop solutions into elegant O(n) single-pass algorithms. Instead of re-examining every possible subarray from scratch, you maintain a "window" that slides across the data, adding one element on the right and removing one on the left.
Core Concept
A window is defined by two pointers: left and right. You expand the window by moving right forward, and contract it by moving left forward. The key insight is that you never move a pointer backward — this is what guarantees O(n) time.
The 2 Sliding Window Archetypes
1. Variable-Size Window
The window grows and shrinks dynamically based on a condition. Used for "longest/shortest subarray/substring" problems.
Template:
left = 0
for right in range(n):
add arr[right] to window state
while window is invalid:
remove arr[left] from window state
left += 1
update answer with current window
Why it works: The right pointer explores every element exactly once. The left pointer only moves forward, also visiting each element at most once. Total work: O(2n) = O(n).
2. Fixed-Size Window
The window always has exactly k elements. Used for "maximum sum of k elements", "average of k elements", and "permutation/anagram in string" problems.
Template:
# Build initial window of size k
for i in range(k):
add arr[i] to window state
# Slide: add right, remove left
for right in range(k, n):
add arr[right] to window state
remove arr[right - k] from window state
update answer
Why it works: Instead of summing k elements from scratch each time (O(nk)), you add the new element and subtract the old one in O(1) per step.
When Sliding Window Fails
- When elements are not contiguous (need dynamic programming or backtracking)
- When you need to consider all possible subsets, not just subarrays
- When the "window validity" condition is not monotonic (shrinking might make an invalid window valid again)
- When the problem requires sorting or random access within the window
Common Mistakes
- Forgetting to update state when shrinking: When you move
left, you must undo whatever you did when you added that element - Off-by-one in fixed windows: The window
[left, right]has sizeright - left + 1, notright - left - Not handling the empty string/array: Always check for
n == 0edge case - Updating answer at the wrong time: For "minimum" problems, update after shrinking. For "maximum" problems, update after expanding
- Using the wrong window type: If the problem says "exactly k" or "of size k", use fixed window. If "longest" or "shortest", use variable window
When to Use
Template
1# --- Variable-Size Window (Longest Valid) ---2def longest_window(arr):3"""Find the longest subarray satisfying some condition"""4left = 05best = 06state = {} # or set(), counter, etc.78for right in range(len(arr)):9# Expand: add arr[right] to window state10# state.update(...)1112# Shrink: while window is invalid13while not valid(state):14# Remove arr[left] from state15left += 11617# Update answer18best = max(best, right - left + 1)1920return best2122# --- Fixed-Size Window ---23def fixed_window(arr, k):24"""Process all windows of size k"""25# Build first window26window = sum(arr[:k])27best = window2829# Slide30for i in range(k, len(arr)):31window += arr[i] - arr[i - k]32best = max(best, window)3334return best
Syntax Reference
1# --- Variable-Size Window ---2left = 03for right in range(len(arr)):4# expand: add arr[right] to window5window_state.add(arr[right])67while not is_valid(window_state):8# shrink: remove arr[left] from window9window_state.remove(arr[left])10left += 11112# update answer (window [left..right] is valid)13answer = max(answer, right - left + 1)1415# --- Fixed-Size Window ---16# Build first window17window_sum = sum(arr[:k])18best = window_sum1920# Slide the window21for right in range(k, len(arr)):22window_sum += arr[right] - arr[right - k]23best = max(best, window_sum)
Common Recipes
1# --- Max Profit (Buy & Sell Stock) ---2def max_profit(prices):3min_price = float('inf')4max_profit = 05for price in prices:6min_price = min(min_price, price)7max_profit = max(max_profit, price - min_price)8return max_profit910# --- Longest Substring Without Repeats ---11def length_of_longest_substring(s):12char_index = {}13left = 014max_len = 015for right in range(len(s)):16if s[right] in char_index and char_index[s[right]] >= left:17left = char_index[s[right]] + 118char_index[s[right]] = right19max_len = max(max_len, right - left + 1)20return max_len2122# --- Fixed Window: Max Sum of K Elements ---23def max_sum_k(arr, k):24window = sum(arr[:k])25best = window26for i in range(k, len(arr)):27window += arr[i] - arr[i - k]28best = max(best, window)29return best3031# --- Minimum Window Substring ---32from collections import Counter33def min_window(s, t):34need = Counter(t)35have, required = 0, len(need)36left = 037result = ""38for right in range(len(s)):39c = s[right]40if c in need:41need[c] -= 142if need[c] == 0:43have += 144while have == required:45window = s[left:right + 1]46if not result or len(window) < len(result):47result = window48if s[left] in need:49need[s[left]] += 150if need[s[left]] > 0:51have -= 152left += 153return result
Complexity
| Operation | Time | Space |
|---|---|---|
| Fixed window (sum/avg) | O(n) | O(1) |
| Longest substring (no repeats) | O(n) | O(min(n, charset)) |
| Minimum window substring | O(n) | O(charset) |
| Permutation in string | O(n) | O(charset) |
| Sliding window maximum | O(n) | O(k) |
Watch Out
- ✗When elements are not contiguous (need dynamic programming or backtracking)
- ✗When you need to consider all possible subsets, not just subarrays
- ✗When the "window validity" condition is not monotonic (shrinking might make an invalid window valid again)
- ✗When the problem requires sorting or random access within the window
- ✗Forgetting to update state when shrinking: When you move `left`, you must undo whatever you did when you added that element
- ✗Off-by-one in fixed windows: The window `[left, right]` has size `right - left + 1`, not `right - left`
- ✗Not handling the empty string/array: Always check for `n == 0` edge case
- ✗Updating answer at the wrong time: For "minimum" problems, update after shrinking. For "maximum" problems, update after expanding
- ✗Using the wrong window type: If the problem says "exactly k" or "of size k", use fixed window. If "longest" or "shortest", use variable window