Pattern 01: Two Pointers (Opposite Ends)
Pattern guideUpdated
On this page
- 0. The Template (Copy-Paste Skeleton)
- Which template?
- The 3 rules that prevent every bug
- 1. Pattern Identification & Logic
- How to Identify (Interview Triggers)
- The Algorithm (Pseudocode)
- The ‘Trick’ to Know
- 2. Java Implementation (Brute Force vs. Optimal)
- Example Problem: Two Sum II (Sorted Array) - LC 167
- Java Architecture Insights
- 3. Mental Model & Visualization
- ASCII Diagram (Two Sum II, target = 9)
- Senior Mental Trigger
- 4. Curated Problem Lists
- Commonly Asked (Bread & Butter)
- FAANG ‘Aha!’ Level (Hard/Unintuitive)
- 5. Time & Space Complexity Table
0. The Template (Copy-Paste Skeleton)
Memorise one skeleton. Every opposite-ends problem is this loop with the
if/elsebody swapped.
// TEMPLATE A — OPPOSITE ENDS (converging pointers) on a SORTED array
int twoPointers(int[] arr, int target) {
int left = 0, right = arr.length - 1;
while (left < right) { // '<' not '<=' — l==r is a single element
int sum = arr[left] + arr[right]; // KNOB: whatever "current value" means
if (sum == target) return ...; // found
else if (sum < target) left++; // need BIGGER → move left rightwards
else right--; // need SMALLER → move right leftwards
}
return -1;
}
// TEMPLATE B — SAME DIRECTION (fast/slow write pointer) — in-place filtering
int removeInPlace(int[] arr) {
int slow = 0; // next position to WRITE
for (int fast = 0; fast < arr.length; fast++) { // scans/READS everything
if (keep(arr[fast])) {
arr[slow++] = arr[fast]; // compact survivors to the front
}
}
return slow; // new logical length
}
// TEMPLATE C — EXPAND FROM CENTER (palindromes)
for (int center = 0; center < s.length(); center++) {
expand(s, center, center); // odd-length palindrome
expand(s, center, center + 1); // even-length palindrome
}
void expand(String s, int l, int r) {
while (l >= 0 && r < s.length() && s.charAt(l) == s.charAt(r)) { l--; r++; }
// s.substring(l + 1, r) is the palindrome; length = r - l - 1
}
Which template?
| The problem says | Use |
|---|---|
| sorted array, find a pair/triplet summing to X | A |
| container/water/area between two lines | A |
| remove / move / dedupe in place, keep order | B |
| partition (Dutch flag, move zeroes) | B |
| longest palindromic substring | C |
The 3 rules that prevent every bug
while (left < right)— using<=makesleft == rightcompare an element with itself.- Move exactly one pointer per iteration, unless you’re deliberately skipping duplicates.
- Skipping duplicates (3Sum):
while (l < r && arr[l] == arr[l+1]) l++;after recording a hit.
1. Pattern Identification & Logic
How to Identify (Interview Triggers)
- “Find a pair that satisfies a condition” (sum, difference, product)
- Input array is sorted (or can be sorted)
- “Find two elements” with a constraint
- “Reverse” or “Palindrome” keywords
- “Container / Area” problems involving width and height
The Algorithm (Pseudocode)
left = 0
right = array.length - 1
while (left < right):
compute result using array[left] and array[right]
if result == target:
return answer
else if result < target:
left++ // need a bigger value
else:
right-- // need a smaller value
The ‘Trick’ to Know
- Two pointers only work correctly on sorted data for sum/pair problems. If the array isn’t sorted, you must sort it first (O(n log n)) or use a HashMap instead.
- For 3Sum, fix one element and run two pointers on the rest - reducing O(n^3) to O(n^2).
2. Java Implementation (Brute Force vs. Optimal)
Example Problem: Two Sum II (Sorted Array) - LC 167
Brute Force: O(n^2)
class Solution {
public int[] twoSum(int[] numbers, int target) {
for (int i = 0; i < numbers.length; i++) {
for (int j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] == target) {
return new int[]{i + 1, j + 1};
}
}
}
return new int[]{};
}
}
Optimal: Two Pointers - O(n)
class Solution {
public int[] twoSum(int[] numbers, int target) {
int left = 0, right = numbers.length - 1;
while (left < right) {
int sum = numbers[left] + numbers[right];
if (sum == target) {
return new int[]{left + 1, right + 1};
} else if (sum < target) {
left++;
} else {
right--;
}
}
return new int[]{};
}
}
Java Architecture Insights
- Why not
ArrayList? We use primitiveint[]because there’s no need for dynamic sizing. Avoids autoboxing overhead. - Why
while (left < right)not<=? Two distinct elements needed - they can never be the same index.
3. Mental Model & Visualization
ASCII Diagram (Two Sum II, target = 9)
Array: [2, 3, 5, 7, 11]
L R sum = 2+11 = 13 > 9 → R--
L R sum = 2+7 = 9 → FOUND!
Result: indices [1, 4]
Senior Mental Trigger
“Sorted input + pair search = squeeze from both ends.”
4. Curated Problem Lists
Commonly Asked (Bread & Butter)
| # | Problem | Difficulty |
|---|---|---|
| LC 125 | Valid Palindrome | Easy |
| LC 167 | Two Sum II (Input Array Is Sorted) | Medium |
| LC 283 | Move Zeroes | Easy |
| LC 344 | Reverse String | Easy |
| LC 977 | Squares of a Sorted Array | Easy |
FAANG ‘Aha!’ Level (Hard/Unintuitive)
| # | Problem | Difficulty |
|---|---|---|
| LC 11 | Container With Most Water | Medium |
| LC 15 | 3Sum | Medium |
| LC 42 | Trapping Rain Water | Hard |
| LC 75 | Sort Colors (Dutch National Flag) | Medium |
| LC 16 | 3Sum Closest | Medium |
5. Time & Space Complexity Table
| Approach | Time | Space | Notes |
|---|---|---|---|
| Brute Force | O(n^2) | O(1) | Nested loop checking all pairs |
| Two Pointers | O(n) | O(1) | Single pass with two indices |
| HashMap | O(n) | O(n) | Alternative when array unsorted |