Sliding Window & Two Pointers

Efficient techniques for array and string problems.


1. The Sliding Window Technique

The Sliding Window technique is used for problems that involve finding a subarray or substring that satisfies a certain condition. Instead of re-calculating from scratch for every possible subarray, we cleverly slide a "window" over the data, adding an element at one end and removing an element at the other.

Example 1: Maximum Sum Subarray of Fixed Size k

Problem: Given an array of integers and a number `k`, find the maximum sum of a subarray of size `k`.

Approach & Algorithm

  1. Initialize: Set `maxSum = 0`, `windowSum = 0`, and `windowStart = 0`.
  2. Iterate: Loop through the array with a `windowEnd` pointer from 0 to the end.
  3. Expand Window: Add the element at `windowEnd` to `windowSum`.
  4. Check Window Size: Once the window size is `k` (i.e., `windowEnd >= k - 1`):
    • Compare `windowSum` with `maxSum` and update `maxSum` if `windowSum` is larger.
    • Slide: Subtract the element at `windowStart` from `windowSum` and increment `windowStart`.
  5. Return: After the loop, `maxSum` holds the result.

public class MaxSumSubarray {
    public static int findMaxSumSubarray(int[] arr, int k) {
        if (arr.length < k) { return -1; }
        int maxSum = 0, windowSum = 0, windowStart = 0;
        for (int windowEnd = 0; windowEnd < arr.length; windowEnd++) {
            windowSum += arr[windowEnd];
            if (windowEnd >= k - 1) {
                maxSum = Math.max(maxSum, windowSum);
                windowSum -= arr[windowStart];
                windowStart++;
            }
        }
        return maxSum;
    }
}
            

Dry Run: `arr = [2, 1, 5, 1, 3, 2]`, `k = 3`

windowEnd Window windowSum maxSum Action
0[2]20Add `arr[0]` (2).
1[2, 1]30Add `arr[1]` (1).
2[2, 1, 5]88Window full. `maxSum = max(0, 8)`. Slide: `sum -= arr[0]` (2).
3[1, 5, 1]78Window slides. `maxSum = max(8, 7)`. Slide: `sum -= arr[1]` (1).
4[5, 1, 3]99Window slides. `maxSum = max(8, 9)`. Slide: `sum -= arr[2]` (5).
5[1, 3, 2]69Window slides. `maxSum = max(9, 6)`. Slide: `sum -= arr[3]` (1).

Example 2: Minimum Swaps to Group Elements ≤ k

Problem: You're given an array `arr[]` and a number `k`. Bring all elements less than or equal to `k` together with the minimum number of swaps.

Approach & Algorithm

  1. Find Window Size: Count all elements `≤ k` in the array. This count is our fixed `windowSize`.
  2. Analyze First Window: Count the "bad" elements (`> k`) in the initial window of size `windowSize`. This is your initial `minSwaps`.
  3. Slide Window: Iterate from `windowSize` to the end of the array.
  4. Update Bad Count: In each step, if the element leaving the window was bad, decrement the count. If the element entering is bad, increment the count.
  5. Track Minimum: After each slide, update `minSwaps = Math.min(minSwaps, currentBadCount)`.
  6. Return: `minSwaps` is the final answer.

public class MinSwaps {
    public static int minSwaps(int[] arr, int k) {
        int windowSize = 0;
        for (int val : arr) { if (val <= k) windowSize++; }
        int badCount = 0;
        for (int i = 0; i < windowSize; i++) { if (arr[i] > k) badCount++; }
        int minSwaps = badCount;
        for (int i = windowSize; i < arr.length; i++) {
            if (arr[i - windowSize] > k) badCount--;
            if (arr[i] > k) badCount++;
            minSwaps = Math.min(minSwaps, badCount);
        }
        return minSwaps;
    }
}
            

Dry Run: `arr = [10, 12, 1, 2, 14, 3, 16]`, `k = 5`

Elements `≤ 5` are `{1, 2, 3}`, so `windowSize = 3`.

Window EndCurrent Window`badCount``minSwaps`Action
2`[10, 12, 1]`22Initial window has two "bad" elements (>5).
3`[12, 1, 2]`11Leaving `10` (bad) decs count. Entering `2` (good) no change. `min = min(2,1)=1`
4`[1, 2, 14]`11Leaving `12` (bad) decs count. Entering `14` (bad) incs count. No change overall.
5`[2, 14, 3]`11Leaving `1` (good). Entering `3` (good). No change.
6`[14, 3, 16]`21Leaving `2` (good). Entering `16` (bad) incs count. `min = min(1,2)=1`.

Example 3 (Variable Size Window): Longest Subarray with Sum `k`

Problem: Given an array of positive integers and a target sum `k`, find the length of the longest subarray whose elements sum up to `k`.

Approach & Algorithm

  1. Initialize: Set `windowStart = 0`, `windowSum = 0`, `maxLength = 0`.
  2. Expand Window: Loop with `windowEnd`. Add `arr[windowEnd]` to `windowSum`.
  3. Shrink Window: While `windowSum > k`, subtract `arr[windowStart]` from `windowSum` and increment `windowStart`.
  4. Check for Solution: If `windowSum == k`, update `maxLength` with the current window size (`windowEnd - windowStart + 1`).
  5. Return: After the loop, `maxLength` is the answer.

public class LongestSubarrayWithSumK {
    public static int findLength(int[] arr, int k) {
        int windowStart = 0, windowSum = 0, maxLength = 0;
        for (int windowEnd = 0; windowEnd < arr.length; windowEnd++) {
            windowSum += arr[windowEnd];
            while (windowSum > k) {
                windowSum -= arr[windowStart];
                windowStart++;
            }
            if (windowSum == k) {
                maxLength = Math.max(maxLength, windowEnd - windowStart + 1);
            }
        }
        return maxLength;
    }
}
            

Dry Run: `arr = [4, 1, 1, 1, 2, 3, 5]`, `k = 5`

windowEndWindowwindowSummaxLengthAction
0[4]40Expand.
1[4, 1]52Expand. Sum is 5. `maxLength = max(0, 2) = 2`.
2[1, 1, 1]6 -> 22Expand (sum=6). Shrink: `sum -= arr[0]` (4), `sum=2`. `start=1`.
3[1, 1, 1]32Expand.
4[1, 1, 1, 2]54Expand. Sum is 5. `maxLength = max(2, 4) = 4`.
5[2, 3]8 -> 34Expand (sum=8). Shrink twice (sum=3). `start=3`.
6[3, 5]8 -> 54Expand (sum=8). Shrink: `sum -= arr[4]`(2),`sum=6`. Shrink: `sum -= arr[5]`(3), `sum=3`. `start=5`. No, wait... let's re-trace 5 & 6.
At `end=4`, `sum=5, start=1, maxL=4`.
`end=5`: `sum=5+arr[5]=8`. `sum>k`. `sum-=arr[1]`=7. `start=2`. `sum>k`. `sum-=arr[2]`=6.`start=3`. `sum>k`. `sum-=arr[3]`=5.`start=4`. `sum==k`, `len=2`. `maxL=4`.
`end=6`: `sum=5+arr[6]=10`. Shrink... final sum=5, `start=6`. `len=1`. `maxL=4`.

2. The Two Pointer Technique

The Two Pointer technique is related to the Sliding Window. It involves using two pointers to iterate through a data structure. The pointers can move in different ways, such as from opposite ends towards each other or in the same direction at different speeds.

Example 1: Find a Pair with Target Sum in a Sorted Array

Problem: Given a sorted array of integers and a target sum `x`, find if there exists a pair of elements in the array that sum up to `x`.

Approach & Algorithm

  1. Initialize Pointers: Set a `left` pointer to the start (index 0) and a `right` pointer to the end of the array.
  2. Loop: Continue as long as `left < right`.
  3. Calculate Sum: Find the `currentSum = arr[left] + arr[right]`.
  4. Compare:
    • If `currentSum == target`, a pair is found. Return `true`.
    • If `currentSum < target`, we need a larger sum, so increment `left`.
    • If `currentSum > target`, we need a smaller sum, so decrement `right`.
  5. End Condition: If the loop finishes without finding a pair, return `false`.

public class PairWithTargetSum {
    public static boolean hasPair(int[] arr, int target) {
        int left = 0, right = arr.length - 1;
        while (left < right) {
            int currentSum = arr[left] + arr[right];
            if (currentSum == target) return true;
            else if (currentSum < target) left++;
            else right--;
        }
        return false;
    }
}
            

Dry Run: `arr = [1, 2, 4, 6, 8, 9]`, `target = 10`

`left` (value)`right` (value)`currentSum`Action
0 (1)5 (9)10`sum == target`. Found pair (1, 9). Return `true`.

Dry Run: `arr = [1, 2, 4, 6, 8, 9]`, `target = 13`

`left` (value)`right` (value)`currentSum`Action
0 (1)5 (9)10`sum < target`. Increment `left`.
1 (2)5 (9)11`sum < target`. Increment `left`.
2 (4)5 (9)13`sum == target`. Found pair (4, 9). Return `true`.

Example 2: Remove Duplicates from a Sorted Array

Problem: Given a sorted array, remove the duplicates in-place such that each unique element appears only once. Return the new length of the array.

Approach & Algorithm

  1. Initialize Pointers: Use a "slow" pointer, `nextNonDuplicate`, starting at index 1. This pointer marks the end of the unique subarray.
  2. Iterate: Use a "fast" pointer `i` to loop through the array from index 1.
  3. Check for Uniqueness: Compare the element at the fast pointer `arr[i]` with the last known unique element `arr[nextNonDuplicate - 1]`.
  4. Place Unique Element: If they are different, it means `arr[i]` is a new unique element. Place it at the `nextNonDuplicate` index and increment `nextNonDuplicate`.
  5. Return Length: After the loop, `nextNonDuplicate` will be the length of the array with unique elements.

public class RemoveDuplicates {
    public static int remove(int[] arr) {
        if (arr.length == 0) return 0;
        int nextNonDuplicate = 1; // Slow pointer
        for (int i = 1; i < arr.length; i++) { // Fast pointer
            if (arr[nextNonDuplicate - 1] != arr[i]) {
                arr[nextNonDuplicate] = arr[i];
                nextNonDuplicate++;
            }
        }
        return nextNonDuplicate;
    }
}
            

Dry Run: `arr = [2, 3, 3, 3, 6, 9, 9]`

`i` (Fast)`nextNonDuplicate` (Slow)Condition `arr[n-1] != arr[i]``arr` stateAction
11`arr[0](2) != arr[1](3)` -> true`[2, 3, 3, 3, 6, 9, 9]``arr[1]=3`, `nND++` -> 2
22`arr[1](3) != arr[2](3)` -> false`[2, 3, 3, 3, 6, 9, 9]`Do nothing.
32`arr[1](3) != arr[3](3)` -> false`[2, 3, 3, 3, 6, 9, 9]`Do nothing.
42`arr[1](3) != arr[4](6)` -> true`[2, 3, 6, 3, 6, 9, 9]``arr[2]=6`, `nND++` -> 3
53`arr[2](6) != arr[5](9)` -> true`[2, 3, 6, 9, 6, 9, 9]``arr[3]=9`, `nND++` -> 4
64`arr[3](9) != arr[6](9)` -> false`[2, 3, 6, 9, 6, 9, 9]`Do nothing.

Loop ends. Return `nextNonDuplicate` which is 4. The first 4 elements are `[2, 3, 6, 9]`.