Say you have 100,000 daily temperature readings and want the sum of every 1,000-day stretch. Computed the obvious way, each of the 99,001 stretches costs 1,000 additions, almost 100 million operations in total. But neighboring stretches share 999 of their 1,000 readings, so every stretch you compute from scratch redoes work you just finished. What if each new window could be built from the previous one?
That question is the whole technique. A Sliding Window uses two pointers to track a sequence of elements in an array or string. The left and right pointers form a window that slides over the elements while analyzing its contents, taking in one element as it enters and dropping one as it leaves instead of starting over. The technique gets compared to a magnifying glass moving across a page, with one correction: a magnifying glass forgets everything the moment it moves on, while the window's carried-over state is the entire point.
Fixed-size sliding windows keep a constant size as they traverse an array. The left and right pointers move together, and at each step, we add one new element and remove the oldest one. Watch it on real values: for nums = [1, 4, 2, 6, 3] and k = 3, the first window sums 1 + 4 + 2 = 7. The next window doesn't re-add anything: it takes the 7, adds the incoming 6, drops the outgoing 1, and lands on 12. One more slide gives 12 + 3 - 4 = 11. Before reading the code below, predict what it returns for exactly that input.
def sliding_window_sums(self, nums: list[int], k: int) -> list[int]:
result = [] # Store the sum of each window
total = 0 # Current sum of the window
# Add the first k numbers to the total sum
for index in range(k):
total += nums[index]
result.append(total) # Store the sum of the first window
# Create left pointer at the beginning
left = 0
# Create right pointer which always moves forward
for right in range(k, len(nums)):
total += nums[right] # Add the number at the right pointer
total -= nums[left] # Remove the number at the left pointer
# Move the left pointer forward
left += 1
# Store the new window sum
result.append(total)
return resultThe answer is the list [7, 12, 11], one sum per window, with only two operations spent on each slide. Here, we start by calculating the first window's sum using the first k elements. As we slide through nums, we add the new element at right and remove the old element at left to maintain our window size. The left pointer follows behind the right pointer, keeping exactly k elements between them. This lets us update each window's sum in O(1) instead of recalculating from scratch.
Dynamic windows adjust their size by moving the left pointer based on conditions in the data. The left and right pointers move independently, and the window grows or shrinks depending on what elements it contains. This flexibility makes dynamic windows the right fit for finding sequences that meet specific criteria, similar to how a camera lens adjusts until the image comes into focus. The lens picture breaks in one way that matters: a lens settles once the image is sharp, while a dynamic window keeps re-tightening every time new data knocks it out of focus. The code below finds the longest substring without repeating characters. Before you read past it, commit to a guess: what does it return for the string "pwwkew"?
def lengthOfLongestSubstring(self, s: str) -> int:
# Create a set to keep track of window, 'res' to track result
window = set()
res = 0
# Create left pointer at the beginning, right pointer always moves forward
left = 0
for right in range(len(s)):
# Get the current letter at the right pointer
letter = s[right]
# While the letter is in window, remove letter at left and shift it forward
while letter in window:
window.remove(s[left])
left += 1
# Add the current letter (at right pointer) to the window
window.add(s[right])
# Calculate the window size
windowSize = right - left + 1
# Update the result using the largest window size we've seen
if windowSize > res:
res = windowSize
return resThe function returns 3, the length of "wke". If you guessed 4, you probably found "pwke", which skips the second letter. That's a subsequence, not a substring, and a window can only ever model contiguous runs, which is exactly why this technique applies. Inside the code, we track unique characters using a set for O(1) lookups. When we find a duplicate, we shrink the window by moving left until the duplicate is gone. The right pointer always moves forward to grow the window, while left only moves to remove duplicates. At each step, we calculate the current window size as right - left + 1 and keep track of the longest valid window we've seen.
Sliding Window uses two pointers to create and maintain a small section of elements. As we process the data, we gather information about elements in the current window to solve a specific problem. The payoff is the one from the temperature readings up top: each new window reuses almost everything the last one computed.
Most sliding window solutions follow a common pattern: the right pointer moves steadily forward using a for loop. The right pointer acts like a scout, moving one step at a time to explore new elements in the sequence. That steady forward movement makes sure we consider every possible window.
The left pointer, on the other hand, moves based on the problem's conditions. Sometimes it follows right immediately to maintain a fixed window size. Other times, it only moves when we need to shrink the window to maintain certain conditions. When finding unique character sequences, for example, left only moves when we need to remove duplicates from the window.
You might look at a while loop nested inside the main for loop and conclude the dynamic version must be O(n²). What actually happens: left never moves backward, so each element enters the window once and leaves at most once. Across the entire run that's at most 2n pointer moves no matter how the inner loop fires, which keeps the whole scan at O(n) time.
left and right, both at the beginning of the array or string. Set up any state you'll need to track information about the window, like a set, a hashmap, or a result variable.right pointer forward one step at a time inside a for loop. Process the new element at the right position and update your window's state with it.left pointer forward to shrink it. Remove or update state for any elements that leave the window through left.Sliding Window patterns typically appear in problems involving sequences in arrays or strings. Watch for problems that involve finding subarrays, counting elements, or calculating consecutive sequences. All of them process elements in order while keeping running information about one contiguous range.
The moment a problem involves analyzing contiguous subarrays or finding patterns in sequential data, sliding window is likely the right approach. The reverse test works too: if the problem lets you skip elements, it's asking about subsequences and a window can't help. Whether you're finding the longest substring of unique characters or the subarray with the largest sum, sliding window lets you process these sequences without redundant recalculations.
The Sliding Window technique is one of the most useful patterns for solving array and string problems. When you spot a problem involving sequential data or subarrays, these practices will help you implement it cleanly:
Before you start, quiz yourself on two things from this page: why does the dynamic window stay O(n) even with a while loop nested inside the for loop, and which two operations replace recomputing a fixed window's sum from scratch? If both answers come back quickly, you're ready. Sliding window turns a lot of "longest/shortest/optimal subarray" brute force into a clean linear scan. The trick is recognizing when a problem has the right shape (contiguous + monotonic shrink/grow). Work through these to internalize fixed vs variable windows and the harder variants with monotonic deques:
Completed | Title | Status | Notes | Solution | |
|---|---|---|---|---|---|
Easy | |||||
Easy | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Medium | |||||
Hard | |||||
Hard | |||||
Hard | |||||
Hard |