Skip to Content
Course content

249: Whiteboard Practice: Two-Pointer and Sliding Window

Click on the "Edit" button in the top corner of the screen to edit your slide content.

Alright, let's get into it. Imagine we're at a whiteboard together. When you're in a technical interview, the "Two-Pointer" and "Sliding Window" patterns are basically your best friends for optimizing array or string problems. But instead of me just giving you the definition, let's actually struggle through a problem and see why these patterns exist.

Let's try this: Given a string, find the length of the longest substring without repeating characters. If I give you "abcabcbb", the answer is 3 ("abc"). If I give you "bbbbb", it's 1 ("b").

The "Brute Force" Impulse

My first instinct—and yours probably is too—is to just check every single possible substring. I'll write two nested loops: one to mark the start of the substring and one to expand the end. I'll throw the characters into a Set to check for duplicates.

public int lengthOfLongestSubstring(String s) {
    int maxLen = 0;
    for (int i = 0; i < s.length(); i++) {
        Set seen = new HashSet<>();
        for (int j = i; j < s.length(); j++) {
            if (seen.contains(s.charAt(j))) break;
            seen.add(s.charAt(j));
            maxLen = Math.max(maxLen, j - i + 1);
        }
    }
    return maxLen;
}

This works. It's correct. But it's also slow. It's $O(n^2)$. If the input string is 100,000 characters long, this code is going to hang. Why? Because I'm throwing away all my hard work. Every time the outer loop moves from index 0 to 1, I'm clearing my HashSet and re-scanning characters I've already seen. That feels wasteful.

Wait, why am I re-scanning?

Let's look at "abcabcbb" again. When my inner loop hits that second 'a' at index 3, I know that any substring starting at index 0 is now invalid. But does that mean I need to start over from index 1? Not really. I already know "bca" is a valid substring. I just need to "shrink" my current window from the left until that first 'a' is gone.

This is the "Aha!" moment for the sliding window. Instead of restarting the search, we just maintain a window that expands to the right and contracts from the left.

Formalizing the Window

Let's try a different approach. I'll use two pointers: left and right. The right pointer will lead the way, expanding our window. If we hit a character we've already seen, we'll slide the left pointer forward until the duplicate is gone. I'm still using a Set, but I'm not clearing it every time.

public int lengthOfLongestSubstring(String s) {
    int left = 0, right = 0, maxLen = 0;
    Set window = new HashSet<>();

    while (right < s.length()) {
        char c = s.charAt(right);
        if (!window.contains(c)) {
            window.add(c);
            right++; // Expand the window
            maxLen = Math.max(maxLen, right - left);
        } else {
            // We hit a duplicate! Shrink from the left.
            window.remove(s.charAt(left));
            left++;
        }
    }
    return maxLen;
}

Now, let's trace this. For "abcabcbb", the right pointer cruises to index 3 (the second 'a'). The Set contains {a, b, c}. Since 'a' is already there, the else block kicks in. We remove the char at left (index 0, which is 'a') and move left to 1. Now the Set is {b, c} and we can finally add that second 'a' and keep moving.

The Efficiency Gain

Check out the difference. In the first version, we had a loop inside a loop. In this version, although we have a while loop and pointers moving, each character is visited at most twice: once by the right pointer and once by the left pointer. That's $O(n)$ time complexity. That is a massive leap in performance.

The pattern is always the same:

  • Expand: Move the right pointer to include new elements.
  • Contract: When a condition is violated (like a duplicate character), move the left pointer to make the window valid again.
  • Capture: Update your result (max length, min sum, etc.) whenever the window is in a valid state.

Once you stop thinking about it as "loops" and start thinking about it as a "sliding frame" over your data, these problems become much more intuitive.




📋 Practical Task

Exercise: Fixed-Size Maximum Sum Subarray

Now it's your turn to apply the sliding window logic. Instead of a variable-sized window, you're going to implement a fixed-size window.

The Challenge: Write a method public int findMaxSum(int[] nums, int k) that finds the maximum sum of any contiguous subarray of size k.

Requirements:

  • Do NOT use nested loops to sum the elements for every possible starting position (that would be $O(n * k)$).
  • Use a sliding window: calculate the sum of the first k elements, then slide the window across the array by adding the next element and subtracting the element that is leaving the window.
  • The time complexity must be $O(n)$.

Test Case: nums = [2, 1, 5, 1, 3, 2], k = 3 should return 9 (because the subarray [5, 1, 3] has the maximum sum).

Rating
0 0

There are no comments for now.

to be the first to leave a comment.