Two Pointers I


1. Move Zeroes

Mental Model:

The core idea is compaction — imagine sliding all non-zero elements to the left, preserving their order, and filling the rest with zeros.
Two pointers achieve this efficiently:

  • insertPos — where the next non-zero should go.
  • i — current scanner.

As we scan, we copy non-zeros to the front and fill the rest with zeros later.
This pattern is known as the “in-place stable partition”.

C++ Solution:

class Solution {
public:
    void moveZeroes(vector<int>& nums) {

        int insertPos = 0;

        for(int i = 0; i < nums.size(); i++) {
            if (nums[i] != 0) {
                nums[insertPos++] = nums[i];
            }
        }

        for (int i = insertPos; i < nums.size(); i++) {
            nums[i] = 0;
        }
        
    }
};

Revision Tip:

  • When asked to “rearrange elements without extra space,” think compaction pattern: move valid elements forward, then fill the rest.
  • Similar pattern: “remove element,” “sort colors,” and “partition array.”

2. Remove Duplicates from Sorted Array

Mental Model:

  • Since the array is sorted, duplicates are consecutive.
  • We can use slow and fast pointers:
    • i — explores new elements (fast).
    • insertPos — next unique element position (slow).
  • Every time a new unique value appears, copy it to insertPos.
  • Think of it as “writing unique elements as you go”.

C++ solution

class Solution {
public:
    int removeDuplicates(vector<int>& nums) {
        int insertPos = 0;

        int i = 0;
        int size = nums.size();

        while (i < size) {
            nums[insertPos++] = nums[i];
            
            int current = nums[i];

            while( i < size && nums[i] == current) {
                i++;
            }
        }

        return insertPos;
        
    }
};

Revision Tip

  • For sorted arrays, duplicates are local — no need for maps or sets.
  • The slow-fast pointer trick generalizes to “remove duplicates,” “compress sequences,” and “merge intervals.”
  • If an operation says “modify array in place,” always look for a write pointer

3. Valid Palindrome

Mental Model:

  • This is a two-direction scan problem — we check characters symmetrically from both ends.
  • The challenge is handling noise (non-alphanumeric characters).
  • Two pointers — one from start, one from end — move inward, skipping irrelevant chars and comparing meaningful ones.
  • It’s a clean example of combining two-pointer traversal + conditional skipping.

C++ Solution:

class Solution {
public:
    bool isPalindrome(string s) {
        int size = s.size();
        int i = 0;
        int j = size - 1;
        
        while (i < j) {
            while(i < j && !std::isalnum(s[i])) {
                i++;
            }

            while(i < j && !std::isalnum(s[j])) {
                j--;
            }

            if (std::tolower(s[i]) != std::tolower(s[j])) {
                return false;
            }

            i++;
            j--;
        }

        return true;
    }
};

Revision Tip:

  • Use two pointers when you need mirrored comparisons.
  • Common pattern for string and array validation problems.
  • When you see “ignore certain characters,” think skip + compare inward.