Sliding Window Technique Explained With Simple Examples

The sliding window technique converts O(n²) brute-force solutions into O(n) by maintaining a "window" of elements and updating it incrementally instead of recomputing from scratch. It's one of the most frequently tested patterns in coding interviews. This guide explains the core intuition, covers both fixed-size and variable-size windows, provides a reusable template, works through the key LeetCode problems, and explains when to use sliding window vs prefix sums vs two pointers.

O(n)

sliding window converts O(n²) brute force to linear time

2 types

fixed-size window and variable-size window

Top 10

one of the most common patterns in coding interviews

Arrays+Strings

primary data structures where sliding window applies

1

The Core Intuition

The key insight behind sliding window: instead of recalculating the entire subarray/substring for every position, maintain a "window" that slides through the data. When the window moves one step right, you only do two operations: add the new element entering from the right, and remove the element leaving from the left.

The sliding window analogy

Imagine looking through a train window. As the train moves, you don't see a completely new scene from scratch — the old scene slides out from one edge and a new scene slides in on the other. The sliding window algorithm does the same with arrays: add a new element on the right, remove the old element on the left. Only O(1) work per step instead of O(k) for a window of size k.

2

Fixed-Size vs Variable-Size Windows

ItemFixed-Size WindowVariable-Size Window
Window sizeConstant k — doesn't changeGrows and shrinks based on a condition
ComplexityO(n) — one pass, one shrink per expandO(n) — each element enters and leaves at most once
PointersCan use single right pointer + left = right - kTwo pointers: left and right, move independently
ConditionShrink when: window size > kShrink when: window violates constraint (duplicates, sum too large, etc.)
ExamplesMax sum subarray of size k, Permutation in stringLongest substring without repeating, Minimum window substring
3

Fixed-Size Sliding Window — Explained Step by Step

Window size k is constant. Slide one step at a time: add the new right element, remove the old left element. This gives O(1) per step instead of O(k) for brute force.

Maximum sum subarray of size k — O(n·k) to O(n)

O(n·k) — recalculate sum for every window

❌ Bad
// O(n·k) brute force: recompute sum for every window from scratch
function maxSumSubarrayBrute(arr, k) {
  let maxSum = -Infinity;
  for (let i = 0; i <= arr.length - k; i++) {
    let windowSum = 0;
    for (let j = i; j < i + k; j++) {  // O(k) per window position
      windowSum += arr[j];
    }
    maxSum = Math.max(maxSum, windowSum);
  }
  return maxSum;
}
// For n=1000, k=100: ~100,000 operations

O(n) — update incrementally: +right, -left

✅ Good
// O(n) sliding window: update sum incrementally
function maxSumSubarray(arr, k) {
  if (arr.length < k) return null;

  // Compute first window sum (positions 0 to k-1)
  let windowSum = 0;
  for (let i = 0; i < k; i++) windowSum += arr[i];
  let maxSum = windowSum;

  // Slide: for each new right element, add it and remove the leftmost
  for (let i = k; i < arr.length; i++) {
    windowSum += arr[i];      // add new element entering from right
    windowSum -= arr[i - k];  // remove old element leaving from left
    maxSum = Math.max(maxSum, windowSum);  // O(1) per step!
  }

  return maxSum;
}

// Example walkthrough:
// arr = [2, 1, 5, 1, 3, 2], k = 3
// Window [2,1,5]: sum=8
// Slide → Window [1,5,1]: sum = 8 + 1 - 2 = 7
// Slide → Window [5,1,3]: sum = 7 + 3 - 1 = 9 ← max
// Slide → Window [1,3,2]: sum = 9 + 2 - 5 = 6
// Result: 9

maxSumSubarray([2, 1, 5, 1, 3, 2], 3); // → 9
4

Variable-Size Sliding Window — Two Pointer Approach

The window size grows and shrinks based on a constraint. Use two pointers (left andright) to define window boundaries. The right pointer always advances. The left pointer advances only to fix constraint violations.

javascriptLongest Substring Without Repeating Characters — LeetCode #3
// O(n) variable window: expand right, shrink left when duplicate found
function lengthOfLongestSubstring(s) {
  const charSet = new Set();  // tracks characters currently in window
  let left = 0;
  let maxLen = 0;

  for (let right = 0; right < s.length; right++) {
    // Shrink window from left until the character at 'right' is no longer a duplicate
    while (charSet.has(s[right])) {
      charSet.delete(s[left]);  // remove leftmost character
      left++;                   // shrink window from left
    }

    charSet.add(s[right]);                         // add new right character
    maxLen = Math.max(maxLen, right - left + 1);   // update max window size
  }

  return maxLen;
}

// Example walkthrough: "abcabcbb"
// right=0: add 'a' → window=["a"], maxLen=1
// right=1: add 'b' → window=["a","b"], maxLen=2
// right=2: add 'c' → window=["a","b","c"], maxLen=3
// right=3: 'a' is in set → remove 'a', left=1 → window=["b","c"] → add 'a', maxLen=3
// right=4: 'b' is in set → remove 'b', left=2 → window=["c","a"] → add 'b', maxLen=3
// ... continues, max stays 3
// Result: 3 ("abc")

lengthOfLongestSubstring("abcabcbb"); // → 3
lengthOfLongestSubstring("pwwkew");   // → 3 ("wke")
lengthOfLongestSubstring("bbbbb");    // → 1
javascriptMinimum Window Substring — LeetCode #76
// O(n) — expand right until all chars found, then shrink left as much as possible
function minWindow(s, t) {
  // Count required characters from target t
  const need = {};
  for (const c of t) need[c] = (need[c] || 0) + 1;

  let have = 0;                         // characters in window matching need count
  const required = Object.keys(need).length;  // distinct chars needed
  const window = {};                    // current window character counts
  let minLen = Infinity;
  let res = '';
  let left = 0;

  for (let right = 0; right < s.length; right++) {
    // Expand: add s[right] to window
    const c = s[right];
    window[c] = (window[c] || 0) + 1;
    if (need[c] && window[c] === need[c]) have++;  // satisfied one more requirement

    // Shrink from left while all requirements are satisfied
    while (have === required) {
      // Update result if current window is smaller
      if (right - left + 1 < minLen) {
        minLen = right - left + 1;
        res = s.slice(left, right + 1);
      }
      // Remove s[left] from window and slide left pointer
      window[s[left]]--;
      if (need[s[left]] && window[s[left]] < need[s[left]]) have--;
      left++;
    }
  }

  return res;
}

minWindow("ADOBECODEBANC", "ABC"); // → "BANC"
minWindow("a", "a");               // → "a"
minWindow("a", "aa");              // → "" (impossible)
5

The Sliding Window Template

javascriptReusable template for both fixed and variable windows
// ─── Template: Variable-size sliding window ──────────────────────────────────
function slidingWindowTemplate(arr) {
  let left = 0;

  // Window state — customize based on problem:
  let windowSum = 0;       // for sum-based problems
  // let windowCount = {};  // for frequency-based problems
  // let windowSet = new Set(); // for uniqueness problems

  let result = 0;  // or -Infinity, Infinity, '' depending on problem

  for (let right = 0; right < arr.length; right++) {
    // STEP 1: Expand window — add arr[right]
    windowSum += arr[right];  // or windowCount[arr[right]]++ etc.

    // STEP 2: Shrink window while constraint is violated
    while (/* window is invalid — customize condition */) {
      // Remove arr[left] from window state
      windowSum -= arr[left];  // or windowCount[arr[left]]-- etc.
      left++;  // shrink from left
    }

    // STEP 3: Update result (window is now valid)
    result = Math.max(result, right - left + 1);  // or windowSum, count, etc.
  }

  return result;
}

// ─── Template: Fixed-size window of size k ────────────────────────────────────
function fixedSlidingWindow(arr, k) {
  // Build first window
  let windowState = /* sum, map, set of arr[0..k-1] */;

  // Initial result from first window
  let result = /* process first window */;

  for (let right = k; right < arr.length; right++) {
    // Add new right element
    // windowState += arr[right];  or add arr[right] to map/set

    // Remove leftmost element (always arr[right - k] for fixed window)
    // windowState -= arr[right - k];  or remove arr[right - k] from map/set

    // Update result
    // result = Math.max(result, windowState);
  }

  return result;
}
6

Common Sliding Window Interview Problems

Max sum subarray of size k (LeetCode #643)

Fixed window. Classic intro problem. Add right, subtract left, track max. O(n) time, O(1) space. Good starting problem for understanding the pattern.

Longest substring without repeating (LeetCode #3)

Variable window with Set for uniqueness. Expand until duplicate, shrink from left to remove it. Very commonly asked in FAANG interviews.

Minimum window substring (LeetCode #76)

Variable window with character frequency map. Expand until all target chars included, shrink to find minimum. Hard difficulty — master this for top-tier interviews.

Sliding window maximum (LeetCode #239)

Fixed window — but needs a monotonic deque for O(1) max queries, giving O(n) overall. Hard difficulty. Deque stores indices in decreasing value order.

Fruit Into Baskets / At most K distinct (LeetCode #904)

Variable window — at most K distinct elements. HashMap frequency count. Shrink from left when distinct count exceeds K. Classic interview variant.

Permutation in string (LeetCode #567)

Fixed window — window of length t. Check if window's character frequencies match t's frequencies. Use character count array (26 elements for lowercase).

When to use sliding window vs prefix sum vs two pointers

Use sliding window when: working with contiguous subarray/substring, the condition is inequality-based (sum ≤ k, at most k distinct), and elements are non-negative (for sum problems). Use prefix sum when: equality condition (sum equals exactly k), need to count subarrays, or elements can be negative. Use general two pointers when: sorted array, palindrome checking, or pointers are not required to be adjacent/contiguous.

Frequently Asked Questions