Sliding Window Algorithm Explained: Master Array Problems for Coding Interviews

Complete guide to sliding window technique in Python. Learn how to solve LeetCode substring and subarray problems in O(n) time with 2 proven patterns. Master fixed and variable window approaches with real interview examples

📅 Published: April 22, 2025 ✏️ Updated: May 30, 2025 By Ojaswi Athghara
#sliding-window #two-pointers #arrays #substrings #interview-prep #leetcode

Sliding Window Algorithm Explained: Master Array Problems for Coding Interviews

The "Aha!" Moment That Changed How I Solve Array Problems

I'll never forget the first time I encountered a "longest substring" problem in an interview. My immediate instinct was to use nested loops—check every substring, compare their properties, keep track of the longest one. It worked, but the interviewer's expression told me everything: "Can you do better?"

That's when I learned about the sliding window technique, and it genuinely felt like discovering a superpower. Suddenly, problems that seemed to require O(n²) nested loops could be solved in a single O(n) pass. The trick? Instead of recomputing everything from scratch for each position, maintain a "window" that slides across your data structure, updating only what changes.

In this guide, I'll share everything I wish someone had told me about the sliding window technique when I started. No fancy theory—just practical patterns, clear examples, and the intuition you need to recognize when to use this powerful approach.

What Makes Sliding Window So Powerful?

Before diving into patterns, let's understand why this technique is such a game-changer. Consider this simple problem: Find the maximum sum of any subarray of size 3 in the array [2, 1, 5, 1, 3, 2].

The brute force approach:

# Check every possible subarray of size 3
max_sum = 0
for i in range(len(arr) - 2):
    current_sum = arr[i] + arr[i+1] + arr[i+2]
    max_sum = max(max_sum, current_sum)

This works, but notice the redundancy. When we move from position 0 to position 1, we're recalculating sums that share two elements:

  • Position 0: [2, 1, 5] → sum = 8
  • Position 1: [1, 5, 1] → sum = 7

We're adding 1, 5, and 1 again, even though we already added 1 and 5 in the previous iteration!

The sliding window approach:

# Calculate first window
window_sum = sum(arr[:3])  # 2 + 1 + 5 = 8
max_sum = window_sum

# Slide: remove left element, add right element
for i in range(3, len(arr)):
    window_sum = window_sum - arr[i-3] + arr[i]
    max_sum = max(max_sum, window_sum)

See the difference? Instead of summing 3 elements repeatedly, we subtract one and add one. This simple optimization transforms O(n × k) into O(n), where k is the window size.

The Two Window Patterns You Must Master

Through solving dozens of sliding window problems, I've identified that virtually all of them fall into two categories. Master these two patterns, and you'll recognize the solution to 95% of sliding window problems within seconds.

Pattern 1: Fixed-Size Window

When to recognize:

  • Problem explicitly gives you a window size (K, or "subarray of length...")
  • Asked to find something in "every window of size K"
  • Terms like "maximum sum of subarray of size K" or "first negative in every window"

The template:

def fixed_window_template(arr, k):
    """
    Template for fixed-size sliding window problems.
    Window size remains constant, slide by moving both pointers.
    """
    # Step 1: Calculate initial window
    window_state = calculate_window(arr[:k])
    result = process_window(window_state)

    # Step 2: Slide window
    for i in range(k, len(arr)):
        # Remove leftmost element from window
        remove_element(arr[i - k], window_state)
        # Add rightmost element to window
        add_element(arr[i], window_state)
        # Process current window
        result = update_result(window_state, result)

    return result

Mental model: Imagine a physical window frame sliding across an array. The frame size never changes—you're just shifting it one position at a time, updating what's visible through the frame.

Pattern 2: Variable-Size Window

When to recognize:

  • Problem asks for "longest" or "shortest" substring/subarray satisfying a condition
  • No explicit window size given
  • Conditions like "at most K distinct characters" or "without repeating characters"
  • Terms like "minimum window" or "maximum length"

The template:

def variable_window_template(arr):
    """
    Template for variable-size sliding window problems.
    Window expands/contracts based on conditions.
    """
    left = 0
    window_state = initialize()
    result = None

    # Expand window by moving right pointer
    for right in range(len(arr)):
        # Add current element to window
        add_element(arr[right], window_state)

        # Contract window while condition is violated
        while condition_violated(window_state):
            remove_element(arr[left], window_state)
            left += 1

        # Update result with current valid window
        result = update_result(right - left + 1, result)

    return result

Mental model: Think of an accordion or rubber band. You're constantly trying to expand it (move right pointer) to explore more elements, but when it violates a condition, you contract it (move left pointer) until it's valid again.

Fixed-Size Window: Practical Examples

Let me walk you through three progressively challenging fixed-window problems to build your intuition.

Example 1: Maximum Sum Subarray of Size K

LeetCode #643 - Maximum Average Subarray I

This is the "hello world" of sliding windows—the problem I showed earlier.

def max_sum_subarray(arr, k):
    """
    Find maximum sum of any subarray of size k.
    Classic fixed-size window pattern.
    """
    if k > len(arr) or k <= 0:
        return 0

    # Calculate sum of first window
    window_sum = sum(arr[:k])
    max_sum = window_sum

    # Slide window: subtract left element, add right element
    for i in range(k, len(arr)):
        window_sum = window_sum - arr[i - k] + arr[i]
        max_sum = max(max_sum, window_sum)

    return max_sum

Test cases:

  • max_sum_subarray([1, 4, 2, 10, 23, 3, 1, 0, 20], 4) → 39 (10+23+3+1)
  • max_sum_subarray([2, 3, 5, 1, 6], 2) → 8 (1+6 is incorrect, should be 7 at 1+6)

Key insight: We maintain only one piece of state (window_sum) that updates incrementally. This is the essence of sliding window—incremental updates instead of full recalculations.

Example 2: First Negative Number in Every Window

This problem introduces a new concept: maintaining a queue of relevant elements as the window slides.

Problem: Given an array and window size K, find the first negative number in each window. If no negative exists, return 0.

from collections import deque

def first_negative_in_windows(arr, k):
    """
    Find first negative in each window using deque.
    Deque stores indices of negative numbers in current window.
    """
    result = []
    negatives = deque()  # Store indices of negative numbers

    for i in range(len(arr)):
        # Add current element's index if negative
        if arr[i] < 0:
            negatives.append(i)

        # Window is complete (has k elements)
        if i >= k - 1:
            # Remove indices outside current window
            while negatives and negatives[0] <= i - k:
                negatives.popleft()

            # First element in deque is first negative in window
            if negatives:
                result.append(arr[negatives[0]])
            else:
                result.append(0)

    return result

Test case:

  • first_negative_in_windows([12, -1, -7, 8, -15, 30, 16, 28], 3)
  • Output: [-1, -1, -7, -15, -15, 0]

Key insight: Sometimes we need to maintain additional state about elements in the window. A deque (double-ended queue) is perfect for tracking relevant elements because we can efficiently add to the back and remove from the front as the window slides.

Example 3: Count Occurrences of Anagrams

This problem tests whether you can track more complex state (character frequencies) in a sliding window.

Problem: Given a text and a pattern, count how many anagrams of the pattern exist as substrings in the text.

from collections import Counter

def count_anagrams(text, pattern):
    """
    Count occurrences of pattern's anagrams in text.
    Uses character frequency map in fixed-size window.
    """
    k = len(pattern)
    pattern_count = Counter(pattern)
    window_count = Counter()
    matches = 0
    count = 0

    for i in range(len(text)):
        # Add current character to window
        window_count[text[i]] += 1

        # Track how many characters match pattern frequencies
        if text[i] in pattern_count and window_count[text[i]] == pattern_count[text[i]]:
            matches += 1

        # Window is complete
        if i >= k - 1:
            # Check if all characters match (found anagram)
            if matches == len(pattern_count):
                count += 1

            # Remove leftmost character from window
            left_char = text[i - k + 1]
            if left_char in pattern_count and window_count[left_char] == pattern_count[left_char]:
                matches -= 1
            window_count[left_char] -= 1
            if window_count[left_char] == 0:
                del window_count[left_char]

    return count

Test case:

  • count_anagrams("forxxorfxdofr", "for") → 3
  • The anagrams are: "for", "orf", "ofr"

Key insight: For complex conditions, maintain detailed state (like frequency maps) that updates as the window slides. The pattern is the same—just the state management is more sophisticated.

Variable-Size Window: The Real Challenge

Variable-size windows are where sliding window technique truly shines and where most interview problems lie. The key difference: your window size dynamically adjusts based on conditions.

Example 4: Longest Substring Without Repeating Characters

LeetCode #3 - Longest Substring Without Repeating Characters

This is probably the most famous sliding window problem and a fantastic example of the variable-size pattern.

Problem: Find the length of the longest substring without repeating characters.

def longest_unique_substring(s):
    """
    Find longest substring with all unique characters.
    Expand window, contract when duplicate found.
    """
    char_set = set()
    left = 0
    max_length = 0

    for right in range(len(s)):
        # Expand: add current character
        # But first, contract if it creates duplicate
        while s[right] in char_set:
            char_set.remove(s[left])
            left += 1

        # Add current character to window
        char_set.add(s[right])

        # Update maximum length
        max_length = max(max_length, right - left + 1)

    return max_length

Test cases:

  • longest_unique_substring("abcabcbb") → 3 ("abc")
  • longest_unique_substring("bbbbb") → 1 ("b")
  • longest_unique_substring("pwwkew") → 3 ("wke")

The pattern in action:

For "abcabcbb":

  1. Start: left=0, right=0, window="a", valid ✓, length=1
  2. right=1, window="ab", valid ✓, length=2
  3. right=2, window="abc", valid ✓, length=3
  4. right=3, window="abca", duplicate 'a'! Contract left
  5. Contract: remove 'a', now window="bca", valid ✓, length=3
  6. Continue...

Key insight: The while loop is crucial. We contract the window (move left pointer) until the condition is satisfied again. This ensures we're always working with a valid window.

Example 5: Longest Substring With K Distinct Characters

This problem introduces a counting constraint—a very common pattern in interviews.

Problem: Find the length of the longest substring that contains at most K distinct characters.

from collections import defaultdict

def longest_k_distinct(s, k):
    """
    Find longest substring with at most k distinct characters.
    Track character frequencies in window.
    """
    char_count = defaultdict(int)
    left = 0
    max_length = 0

    for right in range(len(s)):
        # Expand: add current character
        char_count[s[right]] += 1

        # Contract while we have too many 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

        # Update maximum length
        max_length = max(max_length, right - left + 1)

    return max_length

Test cases:

  • longest_k_distinct("aabacbebebe", 3) → 7 ("cbebebe")
  • longest_k_distinct("aaaa", 2) → 4 ("aaaa")

Key insight: Using a frequency map (Counter or dict) allows us to track both what characters are in the window and their counts. We can then easily check conditions like "number of distinct characters."

Example 6: Minimum Window Substring (The Boss Level)

LeetCode #76 - Minimum Window Substring

This is considered a hard problem, but with the variable-size window pattern, it becomes manageable.

Problem: Find the minimum window in string S that contains all characters of string T.

from collections import Counter

def min_window_substring(s, t):
    """
    Find smallest window in s containing all characters of t.
    Expand to find valid window, contract to minimize it.
    """
    if not s or not t:
        return ""

    # Frequency map of characters needed
    needed = Counter(t)
    window_counts = {}

    # Track how many unique characters in t we've satisfied
    have, need = 0, len(needed)

    # Result: (window_length, left, right)
    result = float('inf'), 0, 0
    left = 0

    for right in range(len(s)):
        # Expand: add character from right
        char = s[right]
        window_counts[char] = window_counts.get(char, 0) + 1

        # Check if this character's requirement is now met
        if char in needed and window_counts[char] == needed[char]:
            have += 1

        # Contract: try to minimize window while maintaining validity
        while have == need:
            # Update result if this window is smaller
            if (right - left + 1) < result[0]:
                result = (right - left + 1, left, right)

            # Remove character from left
            char = s[left]
            window_counts[char] -= 1
            if char in needed and window_counts[char] < needed[char]:
                have -= 1
            left += 1

    # Return the minimum window
    length, left, right = result
    return s[left:right + 1] if length != float('inf') else ""

Test case:

  • min_window_substring("ADOBECODEBANC", "ABC") → "BANC"

Key insight: The two-phase approach—expand until valid, then contract to minimize—is the hallmark of variable-size window problems. The nested while loop for contraction is what makes this pattern so powerful.

The Decision Framework: Fixed vs. Variable?

When you encounter a new problem, ask yourself:

Is the window size given or implied?

  • YES → Fixed-size window
  • NO → Check next question

Are you asked to find "longest" or "shortest" satisfying a condition?

  • YES → Variable-size window
  • NO → Check next question

Does the problem involve "at most K" or "exactly K" of something?

  • YES → Variable-size window with counting
  • NO → Might not be a sliding window problem

Does the problem ask about "every window of size K"?

  • YES → Fixed-size window
  • NO → Reevaluate if sliding window is the right approach

Common Pitfalls and How to Avoid Them

Mistake 1: Not Handling Empty Windows

Always check edge cases: empty arrays, k = 0, k > array length.

# Good practice
if not arr or k <= 0 or k > len(arr):
    return appropriate_default_value

Mistake 2: Off-by-One Errors in Window Indices

Remember: window of size k starting at index i contains elements arr[i] through arr[i + k - 1].

# Window starts when we have k elements
if i >= k - 1:  # Not i >= k!
    # Process window

Mistake 3: Forgetting to Update State When Contracting

In variable-size windows, when you move the left pointer, you MUST update your window state.

# Wrong
left += 1

# Right
remove_element_from_state(arr[left])
left += 1

Mistake 4: Not Tracking the Window Range Correctly

For variable-size windows, the current window length is right - left + 1, not right - left.

# Current window: arr[left] to arr[right] inclusive
window_length = right - left + 1

Advanced Patterns and Variations

Once you've mastered the basics, you'll encounter problems that combine sliding window with other techniques:

Pattern A: Sliding Window + Two Pointers

Some problems require maintaining two conditions simultaneously. For example, finding a substring with exactly K distinct characters (not "at most K").

Approach: Find longest with at most K distinct, then subtract longest with at most (K-1) distinct.

Pattern B: Sliding Window + Hashing

When the window needs to track frequencies or sets of elements, combine with hash maps or sets.

Pattern C: Sliding Window + Monotonic Queue/Stack

For problems like "sliding window maximum" (LeetCode #239), you need a deque that maintains elements in monotonic order.

My Practice Strategy

Here's how I went from struggling with sliding window to solving them confidently:

Week 1: Master Fixed-Size Windows

  • Solve 10-15 fixed-size window problems
  • Focus on different types of state: sums, counts, frequencies
  • Practice the template until it's muscle memory

Week 2: Tackle Variable-Size Windows

  • Start with "longest substring" variations
  • Move to "at most K" problems
  • Finally attempt minimum window problems

Week 3: Mix and Match

  • Solve problems without looking at the pattern type first
  • Practice identifying the pattern from the problem statement
  • Time yourself—aim for recognition in 30 seconds, solution in 15 minutes

Real Interview Experience

I was once asked: "Given an array of integers and a number K, find the length of the longest subarray with sum equal to K."

My thought process (out loud):

  1. "This asks for 'longest' → likely variable-size window"
  2. "Condition is 'sum equal to K' → I'll track current sum"
  3. "I'll expand by adding elements, contract when sum exceeds K"

I coded the solution in about 10 minutes. The interviewer asked, "How did you know to use sliding window so quickly?" I explained the pattern recognition framework, and they were genuinely impressed by the systematic approach.

Beyond the Interview: Real-World Applications

Sliding window isn't just an interview trick—it's used in:

  • Network protocols: TCP sliding window for flow control
  • Image processing: Applying filters with sliding kernels
  • Time series analysis: Moving averages and rolling statistics
  • Streaming data: Processing real-time data with bounded memory

Understanding the technique deeply helps you recognize these patterns in real systems.

Conclusion: Your Sliding Window Mastery Roadmap

The sliding window technique is one of those rare algorithms that genuinely simplifies complex problems. Here's what you need to remember:

  1. Two patterns rule them all: Fixed-size and variable-size windows cover 95% of problems
  2. Pattern recognition is key: Learn to identify sliding window problems from the problem statement
  3. State management matters: Know what to track (sums, counts, frequencies, sets)
  4. Practice the templates: Muscle memory makes implementation trivial

Start with simple fixed-size problems. Once those feel easy, move to variable-size. Within 2-3 weeks of focused practice, you'll be solving hard-level sliding window problems confidently.

The best part? Once you internalize this pattern, you'll start seeing opportunities to apply it everywhere—in interviews, in real code, even in daily problem-solving.

Remember: every expert was once a beginner who kept practicing. The sliding window technique isn't magic—it's just a clever way to avoid redundant work. And now you know the secret.

Happy coding, and may your windows always slide smoothly!


If you found this guide helpful, share it with others preparing for technical interviews. The sliding window technique is one of those "aha!" moments that makes everything click into place. If this guide helped you master sliding window technique or solve subarray problems, I'd love to hear about it! Connect with me on Twitter or LinkedIn.

Support My Work

If this guide helped you master the sliding window technique, transformed how you approach array problems, or gave you confidence for your coding interviews, I'd really appreciate your support! Creating comprehensive, free content like this takes significant time and effort. Your support helps me continue sharing knowledge and creating more helpful resources for students like you.

☕ Buy me a coffee - Every contribution, big or small, means the world to me and keeps me motivated to create more content!


Cover image by Amel Majanovic on Unsplash

Related Blogs

Ojaswi Athghara

SDE, 4+ Years

Š ojaswiat.com 2025-2027