Pattern GuideThe Sliding Window
Algorithm Pattern

The Sliding Window

"The magic of incremental updates. Never restart what you can reuse."

10 min read
Fundamental to Medium
Efficiency: O(N) Time
01
THE PROBLEM

A Window Over a Bigger List

You have a long list, and you keep asking a question about a small connected stretch of it — the biggest sum of any 3 numbers in a row, the longest run with no repeated letter. There are thousands of such stretches, and you want the best one.

The obvious way: take each stretch, walk it, and work out its answer from scratch. For "biggest sum of K in a row" over N numbers that is N stretches times K work each — O(N×K). And it is wasteful in a way you can feel: when you slide one step to the right, the new stretch shares almost every number with the old one. You just re-added K numbers when only two of them actually changed.

That repeated work is the whole opportunity. Remove it and O(N×K) collapses to O(N).
02
THE INSIGHT

Don't Rebuild — Slide

🪟

Keep a running summary, update only the edges

Hold a window over the list — a left edge and a right edge — and keep a small running summary of what is inside it: a sum, a count of letters, whatever the question needs.

When the window moves one step right, you do not recompute. You add the one element that just entered on the right and subtract the one that just left on the left. Two operations per step, no matter how wide the window is. The K numbers in the middle never get touched again.

The invariant — the one promise: everything between left and right is always a valid stretch for the question. You grow the right edge to explore; the moment the rule breaks, you shrink the left edge until the promise holds again. Keep it, and the answer is always sitting in front of you — everything else is just bookkeeping.
03
SEE IT

Watch the Waste Disappear

Step through both. The Naive run re-adds every number in the window at each stop; the Sliding Window run only touches the two that changed. Watch the operation counter.

INDEX 0
1
+
INDEX 1
2
+
INDEX 2
3
+
INDEX 3
4
INDEX 4
5
INDEX 5
6
Mode
DELTA UPDATING
Active Sum
6
Ops So Far
+3 / −0

Initial window [0,1,2]: add all 3 once: 1 + 2 + 3 = 6

1 / 4
04
SHAPE ONE

A Fixed-Width Window

Same window, first shape: it never changes size — always exactly K wide. Grow the right edge by one, then immediately shrink the left edge by one to hold the width. Reach for this when the question fixes the size: the max sum of K in a row, or an anagram of a fixed word.

Fixed Trace Demo: Max Sum Subarray of size K = 3

0
2
L,R
1
1
2
5
3
1
4
3
5
2
function maxSubarraySum(arr, k) {
let left = 0, sum = 0, maxSum = 0;
for (let right = 0; right < arr.length; right++) {
sum += arr[right]; // 1️⃣ Expand
if (right - left + 1 === k) {
maxSum = Math.max(maxSum, sum); // 2️⃣ Record
sum -= arr[left]; // 3️⃣ Shrink
left++; // 4️⃣ Slide
}
}
return maxSum;
}
Variables Monitor
left:0
right:0
sum:0
maxSum:0
Initialize left pointer to 0, running sum to 0, and maxSum result tracker to 0.

The Loop, Four Moves

1️⃣
ExpandAdd arr[right] to incorporate the entering element.
2️⃣
ConditionCheck if the active window width reaches size K.
3️⃣
Record / EvaluateCompare and update maxSum with the current window sum.
4️⃣
Slide windowSubtract arr[left] from sum, and increment left forward.
🎯 Target Scenario Signals:
Max/Min Subarray SumAnagram matchingFixed-length sequences
fixed_window.js
1
let left = 0;
2
let state = initializeState();
3
let result = initializeResult();
4
5
for (let right = 0; right < arr.length; right++) {
6
// 1️⃣ Expand
7
add(state, arr[right]);
8
9
// 2️⃣ Condition: When window reaches size K
10
if (right - left + 1 === k) {
11
// 3️⃣ Record / Evaluate
12
result = updateResult(result, state);
13
14
// 4️⃣ Slide window
15
remove(state, arr[left]);
16
left++;
17
}
18
}
05
SHAPE TWO

A Window That Breathes

Same window, now it changes size. The right edge keeps exploring forward. The moment the stretch breaks the rule — sum over the limit, or a repeated letter — the left edge steps in until the window is valid again. That is the invariant in action, and the answer is the best valid window you ever saw.

Variable Trace Demo: Longest Subarray with Sum ≤ 7

Array: [2, 5, 2, 1, 6]
Limit: 7
0
2
L,R
1
5
2
2
3
1
4
6
function longestSubarray(arr, limit) {
let left = 0, sum = 0, maxLen = 0;
for (let right = 0; right < arr.length; right++) {
sum += arr[right]; // 1️⃣ Expand
while (sum > limit) {
sum -= arr[left]; // 2️⃣ Shrink
left++; // 3️⃣ Contract
}
maxLen = Math.max(maxLen, right - left + 1); // 4️⃣ Record
}
return maxLen;
}
Variables Monitor
left:0
right:0
sum:0
maxLen:0
Initialize left pointer, running sum, and maxLen tracker to 0.

The Loop, Four Moves

1️⃣
ExpandAdd arr[right] to expand the active window.
2️⃣
ShrinkSubtract arr[left] from sum while sum > limit.
3️⃣
ContractIncrement left to shrink the active window frame.
4️⃣
Record / OptimizeRecord the maximum valid length (right - left + 1).
🎯 Target Scenario Signals:
Longest substringMin window containing subSubarray sums <= limit
variable_window.js
1
let left = 0;
2
let state = emptyState();
3
let result = 0;
4
5
for (let right = 0; right < arr.length; right++) {
6
// 1️⃣ Expand
7
addToState(state, arr[right]);
8
9
// 2️⃣ Condition: While invalid
10
while (!isValid(state)) {
11
// 3️⃣ Contract
12
removeFromState(state, arr[left]);
13
left++;
14
}
15
16
// 4️⃣ Record / Optimize
17
result = optimize(result, right - left + 1);
18
}
06
ANALYSIS

Performance Dashboard

TIME EFFICIENCY

O(N)
LINEAR SPEED

Each element is processed exactly twice (once entering on the right, once leaving on the left), making it radically faster than checking every combination.

THE INTUITION

1
Explorer
We move the right boundary forward to scan new values.
2
Monotonicity
For positive numbers, adding items strictly increases the total, establishing a predictable trend.
3
Repair
When constraints fail, advancing the left boundary is the only way to return to a valid state.
🚫

CRITICAL: When it Fails

Sliding Window fails with negative numbers. If subtracting an item makes the sum bigger, the window loses its clear "direction" and doesn't know how to repair itself.

07
CHEAT SHEET

Summary Comparison

Window StyleConstraint ConditionPointers SetupTime / Space Complexity
Fixed SizeSize is strictly Kright expands, left shifts when size matches KO(N) / O(1)
Variable SizeLength adjusts dynamicallyright expands, left contracts while invalidO(N) / O(1)

🔎 When to reach for it

  • 📏
    A contiguous stretch: the question is about a subarray, substring, or sliding block — never a scattered, non-contiguous pick.
  • 📊
    Longest / shortest / best under a rule: longest substring with K distinct letters, shortest subarray reaching a target sum, best average of size K.
  • The naive answer re-scans overlapping stretches: O(N×K) or O(N²) work, most of it repeated — exactly the waste a sliding window removes.
08
PRACTICE

Ready to Practice with Visual Simulations?

Max Sum Subarray of Size KEasy
Longest Substring Without Repeating CharactersMedium
Minimum Size Subarray SumMedium
Minimum Window SubstringHard

"Add from the right, check the rule, and shrink from the left."