Category 2

Sliding Window & Two Pointers

Master sliding window and two-pointer techniques to solve substring, subarray, and array manipulation problems efficiently. Learn templates for expanding/shrinking windows, fast/slow pointers, and greedy two-pointer strategies.

15
Problems
2
Core Patterns
30+
Code Solutions
O(n)
Peak Efficiency
Table of Contents
Theory

Sliding Window & Two Pointers

Two fundamental techniques for optimizing string and array problems from O(n²) to O(n).

Sliding Window

Maintain a window [left, right] that slides across the input. Expand right to include new elements, shrink left when constraints are violated.

💡
Template: EXPAND right → CHECK condition → SHRINK left → UPDATE result

Time Complexity: O(n) — each element enters and exits the window once.

Space Complexity: O(min(n, alphabet size)) for frequency maps.

Two Pointers

Start with one pointer at the beginning and one at the end. Move inward based on comparisons. Works best on sorted data.

💡
Key Insight: If arr[left] + arr[right] is too small, increment left. If too large, decrement right.

Variants: Fast/slow pointers for linked lists, 3+ pointers for complex problems.

2.1
Longest Substring Without Repeating Characters

Find the length of the longest substring without repeating characters.

Sliding Window HashSet LC 3
1

Problem Statement

Given a string s, find the length of the longest substring without repeating characters.

ⓘ
Example: s = "abcabcbb" → 3 (substring "abc")

Approach

Use sliding window with a HashSet to track characters in the current window.

1
Initialize left = 0, maxLen = 0, and a HashSet.
2
Iterate right from 0 to n-1. Add s[right] to the set.
3
While s[right] is a duplicate, remove s[left] and increment left.
4
Update maxLen = max(maxLen, right - left + 1).

C# Implementation

C#
public int LengthOfLongestSubstring(string s)
{
    var charSet = new HashSet<char>();
    int left = 0, maxLen = 0;

    for (int right = 0; right < s.Length; right++)
    {
        while (charSet.Contains(s[right]))
        {
            charSet.Remove(s[left]);
            left++;
        }
        charSet.Add(s[right]);
        maxLen = Math.Max(maxLen, right - left + 1);
    }

    return maxLen;
}

Step-by-Step Trace

Input: s = "abcabcbb"

rightcharwindowmaxLen
0a[a]1
1b[a,b]2
2c[a,b,c]3
3a[b,c,a]3
4b[c,a,b]3
5c[a,b,c]3
6b[b]3
7b[b]3

Complexity Analysis

Time
O(n)
Space
O(min(n, 26))

Edge Cases

  • Empty string: return 0
  • All unique: return n
  • All same: return 1
2.2
Two Sum

Find two numbers that add up to a target value.

HashMap Two Pointers LC 1
2

Problem Statement

Given an array of integers and a target value, return the indices of the two numbers that add up to the target.

ⓘ
Example: nums = [2,7,11,15], target = 9 → [0,1]

Approach (HashMap)

As you iterate, store each number and its index. For each number, check if target - num exists in the map.

1
Create a HashMap to store value → index.
2
For each number, calculate complement = target - num.
3
If complement exists in map, return the two indices.
4
Otherwise, add current num → index to map and continue.

C# Implementation

C#
public int[] TwoSum(int[] nums, int target)
{
    var map = new Dictionary<int, int>();

    for (int i = 0; i < nums.Length; i++)
    {
        int complement = target - nums[i];

        if (map.ContainsKey(complement))
        {
            return new int[] { map[complement], i };
        }

        if (!map.ContainsKey(nums[i]))
        {
            map.Add(nums[i], i);
        }
    }

    return new int[] { };
}

Alternative: Two Pointers (Sorted)

C#
public int[] TwoSumSorted(int[] nums, int target)
{
    int left = 0, right = nums.Length - 1;

    while (left < right)
    {
        int sum = nums[left] + nums[right];

        if (sum == target)
            return new int[] { left, right };
        else if (sum < target)
            left++;
        else
            right--;
    }

    return new int[] { };
}

Complexity Analysis

Time (HashMap)
O(n)
Space
O(n)

Edge Cases

  • Exactly 2 elements: direct comparison
  • No solution: return empty array
  • Same element twice: ensure i ≠ j
2.3
Container With Most Water

Find two vertical lines that form a container holding the most water.

Two Pointers Greedy LC 11
3

Problem Statement

Given an array of integers representing heights, find the two lines that form a container with maximum area.

ⓘ
Example: height = [1,8,6,2,5,4,8,3,7] → 49 (lines at index 1 and 8, height = min(8,7) = 7, width = 7)

Approach

Use two pointers starting at both ends. Area = min(left, right) × (right - left). Move the pointer with smaller height inward (greedy).

1
Set left = 0, right = n-1, maxArea = 0.
2
Calculate area = min(h[left], h[right]) × (right - left).
3
Update maxArea if this area is larger.
4
Move the pointer with smaller height inward; repeat until left ≥ right.

C# Implementation

C#
public int MaxArea(int[] height)
{
    int left = 0, right = height.Length - 1;
    int maxArea = 0;

    while (left < right)
    {
        int h = Math.Min(height[left], height[right]);
        int w = right - left;
        int area = h * w;
        maxArea = Math.Max(maxArea, area);

        if (height[left] < height[right])
            left++;
        else
            right--;
    }

    return maxArea;
}

Why Greedy Works

The height is limited by the shorter line. Moving the taller line inward only decreases width without potentially increasing height, so it's suboptimal. Moving the shorter line might find a taller line that compensates for lost width.

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • All same height: return (n-1) × h
  • Two elements: return min × 1
2.4
MinAvgTwoSlice

Find the starting position of a contiguous slice with minimal average.

Sliding Window Math Codility
4

Problem Statement

Find the starting position of a slice with minimum average. A slice has length ≥ 2.

ⓘ
Example: A = [4,2,2,5,1,5,8] → 1 (slice [2,2] with avg 2)

Key Insight

The minimum average slice has length 2 or 3. If a slice of length ≥ 4 has minimum average, one of its length-2 or length-3 subarrays will also have that or better average.

💡
Why? If avg([i, i+1, i+2, i+3]) is minimal, then at least one of [i, i+1], [i+1, i+2], [i+2, i+3] or [i, i+1, i+2], [i+1, i+2, i+3] must have that same or better average.

Approach

1
Check all slices of length 2 and 3.
2
Track the minimum average and its starting index.
3
Return the starting index of the minimum slice.

C# Implementation

C#
public int MinAvgTwoSlice(int[] A)
{
    double minAvg = double.MaxValue;
    int minIdx = 0;

    // Check all slices of length 2
    for (int i = 0; i < A.Length - 1; i++)
    {
        double avg = (A[i] + A[i + 1]) / 2.0;
        if (avg < minAvg)
        {
            minAvg = avg;
            minIdx = i;
        }
    }

    // Check all slices of length 3
    for (int i = 0; i < A.Length - 2; i++)
    {
        double avg = (A[i] + A[i + 1] + A[i + 2]) / 3.0;
        if (avg < minAvg)
        {
            minAvg = avg;
            minIdx = i;
        }
    }

    return minIdx;
}

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • Array length = 2: return 0
  • Decreasing array: return last valid index
2.5
CountDistinctSlices

Count the number of distinct contiguous slices without repeating elements.

Sliding Window HashSet Codility
5

Problem Statement

Count all contiguous slices with no repeating elements. Cap the result at 1 billion.

ⓘ
Example: A = [3, 4, 5] → 6 (slices: [3], [4], [5], [3,4], [4,5], [3,4,5])

Approach

For each position i, find the longest valid slice starting at i (no repeats). If the slice length is k, it contributes k valid slices ending at or before the right boundary.

1
Use sliding window with HashSet to track seen characters.
2
When a repeat is found, count all slices and shrink from left.
3
For a window of length k, add k to the count (k distinct slices).

C# Implementation

C#
public int CountDistinctSlices(int[] A)
{
    const int MAX = 1000000000;
    var seen = new HashSet<int>();
    int left = 0;
    long count = 0;

    for (int right = 0; right < A.Length; right++)
    {
        while (seen.Contains(A[right]))
        {
            seen.Remove(A[left]);
            left++;
        }
        seen.Add(A[right]);

        // All slices ending at right and starting at or after left
        count += (right - left + 1);

        if (count > MAX)
            return MAX;
    }

    return (int)count;
}

Complexity Analysis

Time
O(n)
Space
O(min(n, alphabet))

Edge Cases

  • All same element: 1 (single slice of length 1)
  • All distinct: n*(n+1)/2
  • Count exceeds 1 billion: cap and return MAX
2.6
3Sum

Find all unique triplets that sum to a target value.

Two Pointers Sort LC 15
6

Problem Statement

Find all unique triplets in an array that sum to zero (or a target).

ⓘ
Example: nums = [-1, 0, 1, 2, -1, -4] → [[-1, -1, 2], [-1, 0, 1]]

Approach

Sort the array. Fix one element and use two pointers to find pairs that complete the triplet.

1
Sort the array.
2
For each element i, find two-sum pairs in the remaining array where sum = -nums[i].
3
Skip duplicates at i, left, and right pointers to ensure unique triplets.
4
Adjust left/right pointers based on sum comparison.

C# Implementation

C#
public IList<IList<int>> ThreeSum(int[] nums)
{
    var result = new List<IList<int>>();
    Array.Sort(nums);

    for (int i = 0; i < nums.Length - 2; i++)
    {
        if (nums[i] > 0) break; // No positive triplets possible
        if (i > 0 && nums[i] == nums[i - 1]) continue; // Skip duplicates

        int left = i + 1, right = nums.Length - 1;
        int target = -nums[i];

        while (left < right)
        {
            int sum = nums[left] + nums[right];

            if (sum == target)
            {
                result.Add(new List<int> { nums[i], nums[left], nums[right] });
                while (left < right && nums[left] == nums[left + 1]) left++; // Skip duplicates
                while (left < right && nums[right] == nums[right - 1]) right--; // Skip duplicates
                left++;
                right--;
            }
            else if (sum < target)
                left++;
            else
                right--;
        }
    }

    return result;
}

Complexity Analysis

Time
O(n²)
Space
O(1) or O(n)

Edge Cases

  • All zeros: [[0,0,0]]
  • No triplet: return empty list
  • Duplicates: handle with skip logic
2.7
Minimum Window Substring

Find the minimum window substring containing all characters of a pattern.

Sliding Window Frequency Map LC 76
7

Problem Statement

Given two strings s and t, find the minimum window substring in s that contains all characters in t.

ⓘ
Example: s = "ADOBECODEBANC", t = "ABC" → "BANC"

Approach

Use sliding window with frequency maps. Expand right until all characters are matched, then shrink left while valid.

1
Create frequency map for pattern characters.
2
Expand right pointer, adding characters to window map.
3
When window is valid (all chars matched), record it and shrink from left.
4
Return the minimum window found.

C# Implementation

C#
public string MinWindow(string s, string t)
{
    if (t.Length > s.Length) return "";

    var dictT = new Dictionary<char, int>();
    var windowCounts = new Dictionary<char, int>();

    foreach (char c in t)
    {
        if (!dictT.ContainsKey(c)) dictT[c] = 0;
        dictT[c]++;
    }

    int required = dictT.Count;
    int formed = 0;
    int left = 0;
    int minLen = int.MaxValue;
    int minLeft = 0;

    for (int right = 0; right < s.Length; right++)
    {
        char c = s[right];
        if (!windowCounts.ContainsKey(c)) windowCounts[c] = 0;
        windowCounts[c]++;

        if (dictT.ContainsKey(c) && windowCounts[c] == dictT[c])
            formed++;

        while (left <= right && formed == required)
        {
            char lc = s[left];
            int windowLen = right - left + 1;

            if (windowLen < minLen)
            {
                minLen = windowLen;
                minLeft = left;
            }

            windowCounts[lc]--;
            if (dictT.ContainsKey(lc) && windowCounts[lc] < dictT[lc])
                formed--;
            left++;
        }
    }

    return minLen == int.MaxValue ? "" : s.Substring(minLeft, minLen);
}

Complexity Analysis

Time
O(|s| + |t|)
Space
O(|t|)

Edge Cases

  • t longer than s: return empty
  • No match: return empty
  • Entire s is the window: return s
2.8
Trapping Rain Water

Calculate how much rainwater can be trapped after raining on an elevation map.

Two Pointers Dynamic LC 42
8

Problem Statement

Given an array of heights, calculate the total amount of rainwater trapped after raining.

ⓘ
Example: height = [0,1,0,2,1,0,1,4,0,2,1,2,0] → 6

Approach: Two Pointers

Water at position i is trapped between leftMax and rightMax. Use two pointers moving inward to track maxima.

1
Set left = 0, right = n-1. Track leftMax and rightMax.
2
Move the pointer with the smaller height. If it's shorter, water is trapped = min(leftMax, rightMax) - height[ptr].
3
Continue until left ≥ right.

C# Implementation

C#
public int Trap(int[] height)
{
    int left = 0, right = height.Length - 1;
    int leftMax = 0, rightMax = 0;
    int water = 0;

    while (left < right)
    {
        if (height[left] < height[right])
        {
            if (height[left] >= leftMax)
                leftMax = height[left];
            else
                water += leftMax - height[left];
            left++;
        }
        else
        {
            if (height[right] >= rightMax)
                rightMax = height[right];
            else
                water += rightMax - height[right];
            right--;
        }
    }

    return water;
}

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • Increasing heights: 0
  • Single peak: depends on surrounding heights
2.9
Longest Repeating Character Replacement

Find the longest substring with at most k character replacements to make all same.

Sliding Window Frequency LC 424
9

Problem Statement

Find the length of the longest substring you could create with at most k replacements.

ⓘ
Example: s = "ABAB", k = 2 → 4 (replace both As or Bs)

Approach

Sliding window: track the frequency of the most common character. If (window size - max freq) ≤ k, the window is valid.

1
Use a frequency map for characters in the window.
2
Track the max frequency. Window is valid if size - maxFreq ≤ k.
3
Expand right; shrink left if window is invalid.
4
Return the maximum valid window size.

C# Implementation

C#
public int CharacterReplacement(string s, int k)
{
    var charCount = new Dictionary<char, int>();
    int left = 0, maxFreq = 0, maxLen = 0;

    for (int right = 0; right < s.Length; right++)
    {
        if (!charCount.ContainsKey(s[right])) charCount[s[right]] = 0;
        charCount[s[right]]++;
        maxFreq = Math.Max(maxFreq, charCount[s[right]]);

        // Number of changes needed = window size - max frequency
        int changes = (right - left + 1) - maxFreq;

        while (changes > k)
        {
            charCount[s[left]]--;
            left++;
            changes = (right - left + 1) - maxFreq;
        }

        maxLen = Math.Max(maxLen, right - left + 1);
    }

    return maxLen;
}

Complexity Analysis

Time
O(n)
Space
O(26)

Edge Cases

  • k = 0: length of longest run of same char
  • k ≥ n-1: return n (can replace all but one)
2.10
Fruit Into Baskets

Maximize fruits collected when using at most 2 distinct basket types.

Sliding Window At Most K LC 904
10

Problem Statement

Given an array of fruit types, maximize fruits collected with at most 2 distinct types.

ⓘ
Example: fruits = [1,2,1,2,1,2,3] → 5 (types 1 and 2: indices 0-4)

Approach

Sliding window with hashmap to track fruit frequencies. Maintain at most 2 distinct fruits.

1
Expand right, adding fruit to the window.
2
If more than 2 distinct fruits, shrink from left until valid.
3
Track the maximum window size (total fruits in valid window).

C# Implementation

C#
public int TotalFruit(int[] fruits)
{
    var fruitCount = new Dictionary<int, int>();
    int left = 0, maxFruits = 0;

    for (int right = 0; right < fruits.Length; right++)
    {
        if (!fruitCount.ContainsKey(fruits[right]))
            fruitCount[fruits[right]] = 0;
        fruitCount[fruits[right]]++;

        while (fruitCount.Count > 2)
        {
            fruitCount[fruits[left]]--;
            if (fruitCount[fruits[left]] == 0)
                fruitCount.Remove(fruits[left]);
            left++;
        }

        maxFruits = Math.Max(maxFruits, right - left + 1);
    }

    return maxFruits;
}

Complexity Analysis

Time
O(n)
Space
O(2) = O(1)

Edge Cases

  • Single fruit type: return n
  • All different: return 2
2.11
Permutation in String

Check if s1 is a permutation of any substring in s2.

Fixed Window Frequency LC 567
11

Problem Statement

Given two strings s1 and s2, return true if s1 is a permutation of a substring in s2.

ⓘ
Example: s1 = "ab", s2 = "eidbaooo" → true (substring "ba")

Approach

Fixed-size sliding window of size len(s1). Compare character frequencies.

1
Create frequency array for s1 (26 chars).
2
Create frequency array for the first window of s2.
3
Slide the window: remove leftmost char, add next char.
4
If frequencies match at any point, return true.

C# Implementation

C#
public bool CheckInclusion(string s1, string s2)
{
    if (s1.Length > s2.Length) return false;

    int[] freq1 = new int[26];
    int[] freq2 = new int[26];

    for (int i = 0; i < s1.Length; i++)
    {
        freq1[s1[i] - 'a']++;
        freq2[s2[i] - 'a']++;
    }

    if (ArraysEqual(freq1, freq2)) return true;

    for (int i = s1.Length; i < s2.Length; i++)
    {
        freq2[s2[i] - 'a']++;
        freq2[s2[i - s1.Length] - 'a']--;

        if (ArraysEqual(freq1, freq2)) return true;
    }

    return false;
}

private bool ArraysEqual(int[] a, int[] b)
{
    for (int i = 0; i < a.Length; i++)
        if (a[i] != b[i]) return false;
    return true;
}

Complexity Analysis

Time
O(|s2| + 26)
Space
O(26) = O(1)

Edge Cases

  • s1 longer than s2: return false
  • s1 = s2: return true
2.12
Maximum Average Subarray I

Find the maximum average of a contiguous subarray of fixed size k.

Sliding Window Fixed Size LC 643
12

Problem Statement

Find the maximum average of all contiguous subarrays of size k.

ⓘ
Example: nums = [1,12,-5,-6,50,3], k = 4 → 12.75 (subarray [12,-5,-6,50])

Approach

Compute sum of first k elements. Slide the window: subtract left element, add new right element.

1
Sum the first k elements.
2
For each new position, remove left element and add right element.
3
Track the maximum sum (equivalently, maximum average).

C# Implementation

C#
public double FindMaxAverage(int[] nums, int k)
{
    double sum = 0;
    for (int i = 0; i < k; i++)
        sum += nums[i];

    double maxSum = sum;

    for (int i = k; i < nums.Length; i++)
    {
        sum += nums[i] - nums[i - k];
        maxSum = Math.Max(maxSum, sum);
    }

    return maxSum / k;
}

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • k = n: return average of entire array
  • k = 1: return max single element
2.13
Subarray Product Less Than K

Count the number of contiguous subarrays with product less than k.

Sliding Window Product LC 713
13

Problem Statement

Count all contiguous subarrays whose product is less than k.

ⓘ
Example: nums = [10,5,2,6], k = 100 → 8 (subarrays: [10], [5], [2], [6], [10,5], [5,2], [2,6], [5,2,6])

Approach

Sliding window: expand right while product < k. When invalid, shrink left. For each position right, all subarrays [left..right] to [right..right] are valid → count = right - left + 1.

1
Maintain a sliding window with product < k.
2
For each right, if product ≥ k, shrink from left.
3
Add (right - left + 1) to count for each valid right position.

C# Implementation

C#
public int NumSubarrayProductLessThanK(int[] nums, int k)
{
    if (k <= 1) return 0;

    int left = 0, product = 1, count = 0;

    for (int right = 0; right < nums.Length; right++)
    {
        product *= nums[right];

        while (product >= k)
        {
            product /= nums[left];
            left++;
        }

        // All subarrays [left..right] to [right..right] have product < k
        count += (right - left + 1);
    }

    return count;
}

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • k ≤ 1: return 0
  • All products < k: return n*(n+1)/2
  • Zeros in array: product becomes 0, always valid
2.14
Sort Colors

Sort an array of colors (0, 1, 2) in-place using three pointers.

Three Pointers Dutch Flag LC 75
14

Problem Statement

Sort an array containing only 0, 1, 2 (colors) in-place without using sort function.

ⓘ
Example: nums = [2,0,2,1,1,0] → [0,0,1,1,2,2]

Approach: Dutch National Flag

Use three pointers: left (0s), mid (current), right (2s).

1
Partition: 0s go to the left, 2s to the right, 1s stay in the middle.
2
If nums[mid] = 0, swap with left and move left/mid right.
3
If nums[mid] = 2, swap with right and move right left (don't move mid yet).
4
If nums[mid] = 1, just move mid right.

C# Implementation

C#
public void SortColors(int[] nums)
{
    int left = 0, mid = 0, right = nums.Length - 1;

    while (mid <= right)
    {
        if (nums[mid] == 0)
        {
            Swap(nums, left, mid);
            left++;
            mid++;
        }
        else if (nums[mid] == 1)
        {
            mid++;
        }
        else // nums[mid] == 2
        {
            Swap(nums, mid, right);
            right--;
        }
    }
}

private void Swap(int[] nums, int i, int j)
{
    int temp = nums[i];
    nums[i] = nums[j];
    nums[j] = temp;
}

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • All same color: no swaps needed
  • Already sorted: still O(n) single pass
2.15
Remove Duplicates from Sorted Array

Remove duplicates in-place and return the new length.

Two Pointers In-Place LC 26
15

Problem Statement

Remove duplicates from a sorted array in-place. Return the length of the array with unique elements.

ⓘ
Example: nums = [1,1,2] → 2, nums = [1,2,uninitialized]

Approach

Use two pointers: left (position to write) and right (position to read). When a new unique element is found, write it to left position and increment left.

1
Set left = 1 (first unique element is at index 0).
2
Iterate right from 1 to n-1.
3
If nums[right] ≠ nums[right-1], copy to nums[left] and increment left.
4
Return left (count of unique elements).

C# Implementation

C#
public int RemoveDuplicates(int[] nums)
{
    if (nums.Length == 0) return 0;

    int left = 1;

    for (int right = 1; right < nums.Length; right++)
    {
        if (nums[right] != nums[right - 1])
        {
            nums[left] = nums[right];
            left++;
        }
    }

    return left;
}

Step-by-Step Trace

Input: nums = [1,1,2,2,3]

rightnums[right]Actionnumsleft
11Skip (same)[1,1,2,2,3]1
22Write to left[1,2,2,2,3]2
32Skip (same)[1,2,2,2,3]2
43Write to left[1,2,3,2,3]3

Complexity Analysis

Time
O(n)
Space
O(1)

Edge Cases

  • Empty array: return 0
  • All unique: return n
  • All duplicates: return 1