,

Contents · Two Pointers & Sliding Window


When to Use Two Pointers / Sliding Window

Use two pointers when the input is sorted or can be sorted and you need a linear scan to compare elements from different positions. Use a sliding window when you need the best/first/minimal subarray fulfilling a constraint, usually with a monotone property as the window grows/shrinks.

  • Time: O(n) typical
  • Space: O(1) to O(Σ) depending on frequency maps
  • Key: Maintain an invariant and adjust pointers in a single pass

Two Pointers: Opposite-Ends & Same-Direction

Opposite-Ends (sorted arrays)

// Two-sum in sorted array: return any pair (i, j) with a[i] + a[j] == target
function twoSumSorted(a, target) {
  let i = 0, j = a.length - 1;
  while (i < j) {
    const s = a[i] + a[j];
    if (s === target) return [i, j];
    if (s < target) i++; else j--;
  }
  return [-1, -1];
}

Same-Direction (merge-like scans)

// Intersection of two sorted arrays (unique elements)
function intersectSorted(a, b) {
  let i = 0, j = 0, out = [];
  while (i < a.length && j < b.length) {
    if (a[i] === b[j]) { out.push(a[i]); i++; j++; }
    else if (a[i] < b[j]) i++; else j++;
  }
  return out;
}

Sliding Window Patterns

Fixed-size window

// Max sum of any subarray of length k
function maxSumFixedWindow(a, k) {
  let sum = 0; for (let i = 0; i < k; i++) sum += a[i];
  let best = sum;
  for (let i = k; i < a.length; i++) {
    sum += a[i] - a[i - k];
    if (sum > best) best = sum;
  }
  return best;
}

Variable-size window with frequency map

// Longest substring with at most K distinct characters
function lengthOfLongestSubstringKDistinct(s, K) {
  let freq = new Map();
  let l = 0, best = 0;
  for (let r = 0; r < s.length; r++) {
    freq.set(s[r], (freq.get(s[r]) || 0) + 1);
    while (freq.size > K) {
      const c = s[l++];
      freq.set(c, freq.get(c) - 1);
      if (freq.get(c) === 0) freq.delete(c);
    }
    best = Math.max(best, r - l + 1);
  }
  return best;
}

Monotonic queue for min/max in window

// Sliding window maximum in O(n)
function maxSlidingWindow(nums, k) {
  const dq = []; // stores indices, decreasing by value
  const out = [];
  for (let i = 0; i < nums.length; i++) {
    while (dq.length && dq[0] <= i - k) dq.shift();
    while (dq.length && nums[dq[dq.length - 1]] <= nums[i]) dq.pop();
    dq.push(i);
    if (i + 1 >= k) out.push(nums[dq[0]]);
  }
  return out;
}

Canonical Examples

  • 3Sum (sorted + opposite-ends): Fix i, then two-pointer on the suffix.
  • Minimum window substring: Variable window with counts and required matches.
  • Longest substring without repeating characters: Variable window with set/map.
  • Fruit into baskets: Window with K=2 distinct types.

Tricks, Invariants, and Pitfalls

  • Invariant: Window always satisfies or minimally violates the constraint.
  • Progress: Move at least one pointer each iteration to avoid infinite loops.
  • Frequency structure: Prefer arrays for ASCII; maps for Unicode.
  • Duplicates: Sort and deduplicate when necessary to control complexity.

Exercises

  1. Given an array and a target sum, find the length of the shortest subarray with sum ≥ target.
  2. Return all unique triplets in an array that sum to zero in O(n^2).
  3. Given a string, find the length of the longest substring with at most K distinct characters.
Clearly define your window and its validity condition. Shrink only while invalid or to improve optimality.