Sliding Window and Two Pointer Interview Patterns

Sliding window and two pointer techniques are among the most frequently tested patterns in coding interviews. They convert O(n²) or O(n³) brute-force solutions into O(n) linear time, and appear in substring, subarray, and two-sequence problems across all major tech company interviews.

Two Pointer: Fundamentals

# Pattern 1: Converging pointers (sorted array, find pair)
def two_sum_sorted(nums: list[int], target: int) -> list[int]:
    left, right = 0, len(nums) - 1
    while left < right:
        s = nums[left] + nums[right]
        if s == target:
            return [left, right]
        elif s  int:
    """LC 26: in-place, return new length"""
    k = 0  # write pointer
    for read in range(len(nums)):
        if k == 0 or nums[read] != nums[k - 1]:
            nums[k] = nums[read]
            k += 1
    return k

Pattern: Three Sum (Sort + Two Pointers)

def threeSum(nums: list[int]) -> list[list[int]]:
    """LC 15: all unique triplets summing to 0 — O(n²)"""
    nums.sort()
    result = []
    for i in range(len(nums) - 2):
        if i > 0 and nums[i] == nums[i-1]:
            continue  # skip duplicate anchors
        left, right = i + 1, len(nums) - 1
        while left < right:
            s = nums[i] + nums[left] + nums[right]
            if s == 0:
                result.append([nums[i], nums[left], nums[right]])
                while left < right and nums[left] == nums[left+1]: left += 1
                while left < right and nums[right] == nums[right-1]: right -= 1
                left += 1; right -= 1
            elif s < 0: left += 1
            else: right -= 1
    return result  # Time O(n²), Space O(1)

Fixed-Size Sliding Window

def max_sum_subarray_k(nums: list[int], k: int) -> int:
    """Maximum sum of any subarray of length k — O(n)"""
    window_sum = sum(nums[:k])
    max_sum = window_sum
    for i in range(k, len(nums)):
        window_sum += nums[i] - nums[i - k]  # slide: add new, remove old
        max_sum = max(max_sum, window_sum)
    return max_sum

def find_all_anagrams(s: str, p: str) -> list[int]:
    """LC 438: All anagram start indices — O(n)"""
    from collections import Counter
    need = Counter(p)
    have = Counter(s[:len(p)])
    result = []
    if have == need:
        result.append(0)
    for i in range(len(p), len(s)):
        # add new char
        have[s[i]] += 1
        # remove old char
        old = s[i - len(p)]
        have[old] -= 1
        if have[old] == 0:
            del have[old]
        if have == need:
            result.append(i - len(p) + 1)
    return result

Variable-Size Sliding Window (Expand/Shrink)

Template for "shortest/longest subarray satisfying condition":

def sliding_window_variable(nums, condition):
    left = 0
    result = float('inf')  # or -inf for longest
    window_state = initial_state
    for right in range(len(nums)):
        # EXPAND: add nums[right] to window
        update(window_state, nums[right])
        # SHRINK: move left until window is valid (for shortest)
        # OR: while window is valid, try to shrink (for longest)
        while condition(window_state):
            result = min(result, right - left + 1)
            remove(window_state, nums[left])
            left += 1
    return result

Minimum Size Subarray Sum

def minSubArrayLen(target: int, nums: list[int]) -> int:
    """LC 209: shortest subarray with sum >= target — O(n)"""
    left = 0
    current_sum = 0
    min_len = float('inf')
    for right in range(len(nums)):
        current_sum += nums[right]
        while current_sum >= target:
            min_len = min(min_len, right - left + 1)
            current_sum -= nums[left]
            left += 1
    return min_len if min_len != float('inf') else 0

Longest Substring Without Repeating Characters

def lengthOfLongestSubstring(s: str) -> int:
    """LC 3 — O(n) with char index tracking"""
    char_index = {}  # char → last seen index
    left = 0
    max_len = 0
    for right, ch in enumerate(s):
        if ch in char_index and char_index[ch] >= left:
            left = char_index[ch] + 1  # jump left past duplicate
        char_index[ch] = right
        max_len = max(max_len, right - left + 1)
    return max_len

Longest Substring with At Most K Distinct Characters

def lengthOfLongestSubstringKDistinct(s: str, k: int) -> int:
    """LC 340 — O(n)"""
    from collections import defaultdict
    count = defaultdict(int)
    left = 0
    max_len = 0
    for right, ch in enumerate(s):
        count[ch] += 1
        while len(count) > k:
            count[s[left]] -= 1
            if count[s[left]] == 0:
                del count[s[left]]
            left += 1
        max_len = max(max_len, right - left + 1)
    return max_len

Pattern: Minimum Window Substring

def minWindow(s: str, t: str) -> str:
    """LC 76: smallest window in s containing all chars of t — O(n+m)"""
    from collections import Counter
    need = Counter(t)
    missing = len(t)  # total chars still needed
    left = 0
    best_left = best_right = -1
    best_len = float('inf')
    for right, ch in enumerate(s):
        if need[ch] > 0:
            missing -= 1
        need[ch] -= 1
        if missing == 0:  # window contains all chars of t
            # shrink from left
            while need[s[left]] < 0:
                need[s[left]] += 1
                left += 1
            # left is now at tightest valid position
            if right - left + 1 < best_len:
                best_len = right - left + 1
                best_left, best_right = left, right
            # advance left to search for better windows
            need[s[left]] += 1
            missing += 1
            left += 1
    return s[best_left:best_right+1] if best_left != -1 else ''

Pattern: Container With Most Water

def maxArea(height: list[int]) -> int:
    """LC 11: converging two pointers — O(n)"""
    left, right = 0, len(height) - 1
    max_water = 0
    while left < right:
        water = min(height[left], height[right]) * (right - left)
        max_water = max(max_water, water)
        # move the shorter wall inward (moving taller can't help)
        if height[left] < height[right]:
            left += 1
        else:
            right -= 1
    return max_water

Pattern: Trapping Rain Water

def trap(height: list[int]) -> int:
    """LC 42: two pointer O(n) time O(1) space"""
    left, right = 0, len(height) - 1
    left_max = right_max = 0
    water = 0
    while left < right:
        if height[left] = left_max:
                left_max = height[left]
            else:
                water += left_max - height[left]
            left += 1
        else:
            if height[right] >= right_max:
                right_max = height[right]
            else:
                water += right_max - height[right]
            right -= 1
    return water

# Key insight: at any position, water trapped = min(max_left, max_right) - height
# Two-pointer approach: whichever side has smaller max determines water at current position

Pattern: Sliding Window Maximum (Monotonic Deque)

from collections import deque

def maxSlidingWindow(nums: list[int], k: int) -> list[int]:
    """LC 239: max of each window — O(n)"""
    dq = deque()  # stores indices, decreasing values
    result = []
    for i, num in enumerate(nums):
        # remove indices outside window
        while dq and dq[0] < i - k + 1:
            dq.popleft()
        # maintain decreasing order (remove smaller elements from back)
        while dq and nums[dq[-1]] = k - 1:
            result.append(nums[dq[0]])  # front is always the max
    return result

Problem Recognition Cheat Sheet

Clue in Problem Pattern
“sorted array”, “find pair/triplet summing to X” Two pointers (converging)
“subarray/substring”, “sum/count/condition” Sliding window (expand/shrink)
“at most K distinct”, “longest without repeating” Variable-size window + hashmap
“fixed size window”, “max/min of all windows” Fixed window or monotonic deque
“linked list cycle”, “find middle” Fast/slow pointers
“remove duplicates in-place”, “partition” Read/write pointers (same direction)

Key Interview Tips

  • When to shrink: For “shortest” problems, shrink when condition is satisfied. For “longest” problems, shrink when condition is violated. The template is the same — just swap the shrink trigger.
  • Avoid comparison of Counter objects: Counter == Counter is O(k) — track a formed count instead (how many unique chars have required frequency) for O(1) per step.
  • Three-pointer problems: Dutch National Flag (3-way partition) uses three pointers — low, mid, high — to sort [0,1,2] in-place in a single pass.
  • Two pointers in 2D: Problems like “search a sorted 2D matrix” — start from top-right corner: move left if value > target, move down if value < target. O(m+n).

{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How do you recognize when to use the sliding window technique?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Use sliding window when the problem asks about a contiguous subarray or substring and involves finding a min/max length, count, or sum satisfying a condition. Key signals: “longest/shortest subarray”, “minimum window”, “at most K distinct characters”, “subarray sum equals target”. The window expands by moving the right pointer and contracts by moving the left pointer. Fixed-size windows apply when K is given explicitly; variable-size windows use a condition to decide when to shrink. If elements are non-negative, you can use two pointers; if negative numbers appear, you may need prefix sums or deques instead.”
}
},
{
“@type”: “Question”,
“name”: “What is the time complexity advantage of sliding window over brute force?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Brute force for subarray problems is O(nu00b2) or O(nu00b3): enumerate all start/end pairs and compute the condition. Sliding window reduces this to O(n) because each element is added to the window exactly once (right pointer moves right) and removed at most once (left pointer moves right) u2014 total pointer movements are at most 2n. For the “minimum window substring” problem, brute force is O(nu00b2 u00d7 m) to check each substring; sliding window with a character count is O(n + m). The key invariant: both pointers only move forward, so you never re-examine an element.”
}
},
{
“@type”: “Question”,
“name”: “How does the monotonic deque solve the sliding window maximum problem in O(n)?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The deque maintains indices of elements in decreasing order of value. When moving the window right: (1) remove indices from the front that are outside the window boundary; (2) remove indices from the back whose values are less than the new element (they can never be the maximum while the new element is in the window); (3) append the new index to the back; (4) the front of the deque is always the index of the current window’s maximum. Each index is added once and removed once u2014 O(n) total. This generalizes to any “sliding window min/max” problem and to problems requiring the next greater element.”
}
}
]
}

  • Shopify Interview Guide
  • Coinbase Interview Guide
  • Atlassian Interview Guide
  • LinkedIn Interview Guide
  • Uber Interview Guide
  • Apple Interview Guide
  • Companies That Ask This

    Scroll to Top