Master the sliding window technique — one of the most powerful array problem-solving patterns. Learn fixed-size and variable-size windows, when to apply this technique, and solve 10 classic interview problems including maximum sum subarray, longest substring without repeats, minimum window substring, and more.
Introduction
The sliding window technique is one of the most powerful patterns for solving array and string problems efficiently. It transforms problems that would naively require O(n²) or O(n³) nested loops into elegant O(n) solutions.
The key insight: instead of recomputing results from scratch for every possible window position, we *slide* the window incrementally — adding one element at the right edge and removing one element at the left edge — and update our result in O(1) per step.
This technique is essential. It appears in a large percentage of medium and hard array/string interview problems.
Type 1 — Fixed-Size Sliding Window
The window has a fixed size k. It slides one position at a time from left to right.
Template:
def fixed_window_template(arr, k):
n = len(arr)
if n < k:
return None # Handle edge case
# Initialize window (first k elements)
window_result = compute(arr[:k]) # Initial window
best = window_result
# Slide the window from position 1 to n-k
for i in range(k, n):
# Add arr[i] (new right element)
# Remove arr[i - k] (old left element that fell off)
window_result = update(window_result, arr[i], arr[i - k])
best = max(best, window_result) # or min, or other operation
return best
Problem 1 — Maximum Sum Subarray of Size K
def max_sum_subarray(arr, k):
"""
Find the maximum sum of any contiguous subarray of exactly k elements.
Time: O(n), Space: O(1)
"""
n = len(arr)
if n < k:
return None
# Compute sum of first window
window_sum = sum(arr[:k]) # O(k)
max_sum = window_sum
# Slide window: add new element, remove old element
for i in range(k, n):
window_sum += arr[i] # Add incoming element
window_sum -= arr[i - k] # Remove outgoing element
max_sum = max(max_sum, window_sum)
return max_sum
print(max_sum_subarray([2, 1, 5, 1, 3, 2], 3)) # 9 (subarray [5,1,3])
print(max_sum_subarray([2, 3, 4, 1, 5], 2)) # 7 (subarray [3,4])
Problem 2 — Average of Subarrays of Size K
def find_averages(arr, k):
"""Find averages of all subarrays of size k."""
n = len(arr)
averages = []
window_sum = sum(arr[:k])
averages.append(window_sum / k)
for i in range(k, n):
window_sum += arr[i]
window_sum -= arr[i - k]
averages.append(window_sum / k)
return averages
print(find_averages([1, 3, 2, 6, -1, 4, 1, 8, 2], 5))
# [2.2, 2.8, 2.4, 3.6, 2.8]
Problem 3 — First Negative in Every Window
from collections import deque
def first_negative_in_window(arr, k):
"""
For each window of size k, find the first negative number.
Use a deque to track negative number positions.
Time: O(n), Space: O(k)
"""
n = len(arr)
result = []
neg_queue = deque() # Stores indices of negatives in current window
for i in range(n):
# Add index if element is negative
if arr[i] < 0:
neg_queue.append(i)
# Remove indices outside current window
while neg_queue and neg_queue[0] < i - k + 1:
neg_queue.popleft()
# Window is complete starting from index k-1
if i >= k - 1:
result.append(arr[neg_queue[0]] if neg_queue else 0)
return result
print(first_negative_in_window([-8, 2, 3, -6, 10], 2)) # [-8, 0, -6, -6]
Type 2 — Variable-Size Sliding Window
The window size is not fixed. It expands to the right and shrinks from the left based on a condition.
Template:
def variable_window_template(arr):
left = 0
result = initial_value
window_state = {} # Could be a hash map, counter, sum, etc.
for right in range(len(arr)):
# Expand window: add arr[right]
window_state = update_add(window_state, arr[right])
# Shrink window while condition is violated
while not is_valid(window_state):
window_state = update_remove(window_state, arr[left])
left += 1
# Update result with current valid window
result = update_result(result, right - left + 1)
return result
Problem 4 — Smallest Subarray with Sum ≥ S
def smallest_subarray_sum(arr, s):
"""
Find the length of the smallest contiguous subarray with sum >= s.
If no such subarray, return 0.
Time: O(n), Space: O(1)
"""
n = len(arr)
min_length = float('inf')
window_sum = 0
left = 0
for right in range(n):
window_sum += arr[right] # Expand window
while window_sum >= s: # Shrink window while sum is valid
min_length = min(min_length, right - left + 1)
window_sum -= arr[left]
left += 1
return min_length if min_length != float('inf') else 0
print(smallest_subarray_sum([2, 1, 5, 2, 3, 2], 7)) # 2 (subarray [5,2])
print(smallest_subarray_sum([2, 1, 5, 2, 8], 7)) # 1 (subarray [8])
Problem 5 — Longest Substring Without Repeating Characters
def length_of_longest_substring(s):
"""
Find the length of the longest substring with all unique characters.
Time: O(n), Space: O(min(m, n)) where m = charset size
"""
char_index = {} # Maps char → its most recent index in window
max_length = 0
left = 0
for right in range(len(s)):
# If char already in window, shrink left past its previous occurrence
if s[right] in char_index and char_index[s[right]] >= left:
left = char_index[s[right]] + 1
char_index[s[right]] = right # Update char's position
max_length = max(max_length, right - left + 1)
return max_length
print(length_of_longest_substring("abcabcbb")) # 3 ("abc")
print(length_of_longest_substring("bbbbb")) # 1 ("b")
print(length_of_longest_substring("pwwkew")) # 3 ("wke")
Trace for "abcabcbb":
| right=0 (a) | window=[a], max=1 |
| right=1 (b) | window=[ab], max=2 |
| right=2 (c) | window=[abc], max=3 |
| right=3 (a): a is in window at idx 0 | left=1, window=[bca], max=3 |
| right=4 (b): b is in window at idx 1 | left=2, window=[cab], max=3 |
| right=5 (c): c is in window at idx 2 | left=3, window=[abc], max=3 |
| right=6 (b): b is in window at idx 4 | left=5, window=[cb], max=3 |
| right=7 (b): b is in window at idx 6 | left=7, window=[b], max=3 |
Problem 6 — Longest Subarray with Sum ≤ K
def longest_subarray_sum_k(arr, k):
"""
Find the longest subarray where sum does not exceed k.
Works for positive numbers.
Time: O(n), Space: O(1)
"""
max_length = 0
window_sum = 0
left = 0
for right in range(len(arr)):
window_sum += arr[right]
while window_sum > k:
window_sum -= arr[left]
left += 1
max_length = max(max_length, right - left + 1)
return max_length
print(longest_subarray_sum_k([1, 2, 1, 0, 1, 1, 0], 4)) # 5
Problem 7 — Longest Subarray with At Most K Distinct Characters
from collections import defaultdict
def longest_k_distinct(s, k):
"""
Longest substring with at most k distinct characters.
Time: O(n), Space: O(k)
"""
char_count = defaultdict(int)
max_length = 0
left = 0
for right in range(len(s)):
char_count[s[right]] += 1 # Add character
# Shrink window if more than k distinct characters
while len(char_count) > k:
char_count[s[left]] -= 1
if char_count[s[left]] == 0:
del char_count[s[left]]
left += 1
max_length = max(max_length, right - left + 1)
return max_length
print(longest_k_distinct("araaci", 2)) # 4 ("araa")
print(longest_k_distinct("cbbebi", 3)) # 5 ("cbbeb")
Type 3 — Two-Pointer Window (Special Case)
Some problems use two pointers where both can move in interesting ways.
Problem 8 — Container With Most Water
def max_area(heights):
"""
Two vertical lines form a container. Find maximum water area.
Two pointers starting at both ends, moving toward each other.
Time: O(n), Space: O(1)
"""
left, right = 0, len(heights) - 1
max_water = 0
while left < right:
# Width = right - left, height = min of two sides
width = right - left
height = min(heights[left], heights[right])
max_water = max(max_water, width * height)
# Move the shorter side inward (moving taller would only decrease width)
if heights[left] < heights[right]:
left += 1
else:
right -= 1
return max_water
print(max_area([1,8,6,2,5,4,8,3,7])) # 49
Problem 9 — Minimum Window Substring (Hard)
from collections import Counter
def min_window_substring(s, t):
"""
Find the smallest substring of s containing all characters of t.
Time: O(|s| + |t|), Space: O(|s| + |t|)
"""
if not t or not s:
return ""
need = Counter(t) # Characters we need and their counts
missing = len(t) # Total characters still needed
best = ""
best_start = 0
left = 0
for right, char in enumerate(s):
if need[char] > 0: # This character is needed
missing -= 1
need[char] -= 1 # We have one more of this character
# Window contains all required characters
if missing == 0:
# Shrink from left as much as possible
while need[s[left]] < 0:
need[s[left]] += 1
left += 1
# Update best window
window = s[left:right + 1]
if not best or len(window) < len(best):
best = window
# Move left to search for next valid window
need[s[left]] += 1
missing += 1
left += 1
return best
print(min_window_substring("ADOBECODEBANC", "ABC")) # "BANC"
print(min_window_substring("a", "a")) # "a"
print(min_window_substring("a", "b")) # ""
Recognizing Sliding Window Problems
A problem is likely solvable with sliding window if it asks about:
| Keyword | Example |
|---|
| "contiguous subarray" | Maximum sum subarray of size k |
| "substring" with a constraint | Longest substring without repeating |
| "window" | Moving average |
| "k elements" | Maximum in every window of size k |
| "minimum/maximum subarray" | Minimum window containing all chars |
| "at most k distinct" | Longest substring with k distinct |
Ask yourself:
- Does the problem involve a contiguous sequence?
- Is there a constraint (sum ≤ k, at most k distinct chars, etc.)?
- Can adding one element on the right and removing one on the left update the state in O(1)?
If yes to all three, sliding window applies.
Problem 10 — Maximum Consecutive Ones with K Flips
def longest_ones(nums, k):
"""
You can flip at most k zeros to ones.
Find the length of the longest subarray of 1s.
Time: O(n), Space: O(1)
"""
left = 0
zeros_in_window = 0
max_length = 0
for right in range(len(nums)):
if nums[right] == 0:
zeros_in_window += 1
# Shrink window if we have more than k zeros
while zeros_in_window > k:
if nums[left] == 0:
zeros_in_window -= 1
left += 1
max_length = max(max_length, right - left + 1)
return max_length
print(longest_ones([1,1,1,0,0,0,1,1,1,1,0], 2)) # 6
print(longest_ones([0,0,1,1,0,0,1,1,1,0,1,1,0,0,0,1,1,1,1], 3)) # 10
Summary
| Type | Window Size | Direction | Key Operation |
|---|
| Fixed Window | Constant k | One direction | Add right, remove left[i-k] |
| Variable Window | Varies | One direction | Expand right, shrink left until valid |
| Two-Pointer | Varies | Both directions | Both pointers move based on condition |
Time complexity: O(n) — each element enters and leaves the window at most once
Space complexity: O(1) for simple windows, O(k) if storing window contents
The sliding window pattern eliminates redundant recomputation — the defining characteristic that makes it so powerful. Master this pattern and a large class of array/string problems becomes straightforward.
*Next Lesson: Array Interview Problems — Applying everything learned about arrays to solve classic and hard interview problems.*