Algorithms

Two Pointers Technique

Two indices walk through a structure โ€” from opposite ends, same direction, or on separate sequences โ€” reducing O(nยฒ) brute force to O(n). The backbone of sorted-array pair problems, in-place partitioning, and linked-list cycle detection.

Foundations ยท Array ยท Linked List ยท Stack ยท Queue ยท HashMap ยท Binary Tree ยท BST ยท Heap ยท Trie ยท Graph ยท Sorting ยท Binary Search ยท Two Pointers ยท Sliding Window ยท Dynamic Prog.
01
Section One ยท Foundation

What is Two Pointers?

1 3 5 7 9 11 13 L โ†’ โ† R L and R converge โ€” every pair considered in O(n)

Two pointers is a technique where two index variables traverse a data structure simultaneously โ€” either moving toward each other from opposite ends, or moving in the same direction at different speeds. The key insight: by choosing which pointer to advance based on a comparison, you avoid examining every pair (O(nยฒ)) and instead visit each element at most once or twice (O(n)). The technique requires some form of order or structure โ€” typically a sorted array, a partitioned condition, or a linked list where relative position matters. Two pointers appears in four common patterns: (1) opposite-end convergence on a sorted array (Two Sum II, Container With Most Water), (2) same-direction read/write for in-place partitioning (Remove Duplicates, Move Zeroes), (3) fast/slow runners on linked lists (cycle detection, middle node), and (4) merging two sorted sequences. Each pattern is a different configuration of the same core idea: two indices, one movement rule, O(n) total.

Analogy: Two people searching a bookshelf from opposite ends. Person L starts at the left and steps right; person R starts at the right and steps left. They compare what they're holding: if the combined weight is too light, L steps right (to a heavier book); if too heavy, R steps left (to a lighter book). They meet in the middle having checked every possible lightest+heaviest pairing โ€” without ever checking middle+middle or restarting from the beginning.
02
Section Two ยท Mental Model

How It Thinks

Array is sorted โ€” increasing from left to right
โ–ถ
Moving the left pointer right INCREASES the sum of the two elements; moving the right pointer left DECREASES it โ€” enabling directed search toward a target sum
Each step advances at least one pointer โ€” either L++ or R-- (or both)
โ–ถ
At most n steps total before L and R meet โ€” every element visited at most once โ€” O(n) guaranteed
The "read" pointer scans ahead while the "write" pointer marks where to place the next valid element
โ–ถ
In-place partitioning: the read pointer finds valid elements, the write pointer compacts them โ€” no extra array needed, O(1) space
Fast pointer moves 2 steps per iteration, slow pointer moves 1 step
โ–ถ
If a cycle exists, fast catches slow inside the cycle โ€” like two runners on a circular track where the faster one always laps the slower one
Two sorted sequences each have a pointer starting at their beginning
โ–ถ
Compare the two pointed elements, take the smaller, advance that pointer โ€” merges two sorted sequences in O(n+m)
Two pointers doesn't require sorted data for all patterns โ€” partitioning and cycle detection work on unsorted data
โ–ถ
The common thread is not "sorted" but "each pointer moves in one direction only" โ€” monotonic pointer movement guarantees termination and O(n) time
03
Section Three ยท Opposite Ends

Pattern 1 โ€” Opposite-End Convergence

Start left pointer at index 0 and right pointer at index n-1. Compare, then advance the pointer that improves the result. The pointers converge toward each other, meeting in the middle. This only works when the array is sorted (or the problem has a similar monotonic property).

Two Sum II โ€” Sorted Array (LC 167)
Find two numbers in a sorted array that sum to target. Return their 1-indexed positions.
Pseudocode
function twoSum(arr, target): L = 0, R = arr.length - 1 while L < R: sum = arr[L] + arr[R] if sum == target: return [L + 1, R + 1] else if sum < target: L = L + 1 // sum too small โ†’ need bigger else: R = R - 1 // sum too big โ†’ need smaller return [-1, -1] // O(n)
Two Sum II โ€” target = 10 on [1, 3, 5, 7, 9]
Step 1: L=0, R=4 1 3 5 7 9 L R sum = 1 + 9 = 10 == target โœ“ FOUND! Why this works on sorted arrays: โ€ข sum < target โ†’ arr[L] is too small โ†’ advance L to a larger value โ€ข sum > target โ†’ arr[R] is too big โ†’ advance R to a smaller value โ€ข sum == target โ†’ done Every step eliminates one element from consideration โ†’ O(n) total
Container With Most Water (LC 11)
Given heights, find two lines that form a container holding the most water. Width = R - L, height = min(arr[L], arr[R]).
Pseudocode
function maxArea(heights): L = 0, R = heights.length - 1 maxWater = 0 while L < R: width = R - L h = min(heights[L], heights[R]) maxWater = max(maxWater, width ร— h) if heights[L] < heights[R]: L = L + 1 // shorter side limits area โ†’ move it else: R = R - 1 return maxWater // O(n)
Why move the shorter side?:
  • The area is limited by the shorter line.
  • Moving the taller line inward can only decrease width without increasing height โ€” guaranteed worse.
  • Moving the shorter line inward decreases width but might find a taller line โ€” the only chance to improve.
04
Section Four ยท Same Direction

Pattern 2 โ€” Read/Write (Same Direction)

Both pointers start at the beginning and move right. The "read" pointer (fast) scans every element. The "write" pointer (slow) marks the position where the next valid element should be placed. This pattern compacts an array in-place โ€” removing duplicates, zeroes, or specific values without extra space.

Remove Duplicates from Sorted Array (LC 26)
Remove duplicates in-place from a sorted array. Return the count of unique elements.
Pseudocode
function removeDuplicates(arr): if arr.length == 0: return 0 write = 1 // first element always unique for read = 1 to arr.length - 1: if arr[read] != arr[read - 1]: // new unique element arr[write] = arr[read] write = write + 1 return write // O(n), O(1) space
Remove Duplicates โ€” [1, 1, 2, 3, 3] โ†’ [1, 2, 3, _, _]
BEFORE 1 1 2 3 3 dup dup AFTER โ€” write = 3 1 2 3 _ _ 3 unique elements read scans all 5 elements โ€” write only advances for unique values arr[0..write-1] = compacted result, no extra space used
Move Zeroes (LC 283)
Move all zeroes to the end while maintaining the relative order of non-zero elements โ€” in-place, O(1) space.
Pseudocode
function moveZeroes(arr): write = 0 for read = 0 to arr.length - 1: if arr[read] != 0: arr[write] = arr[read] write = write + 1 while write < arr.length: // fill remaining with zeroes arr[write] = 0 write = write + 1 // O(n), O(1) space
The read/write pattern works for any filter condition:
  • Remove duplicates (arr[read] != arr[write-1]), move zeroes (arr[read] != 0), remove element (arr[read] != val), remove even numbers โ€” the structure is identical.
  • Only the condition changes.
05
Section Five ยท Fast & Slow

Pattern 3 โ€” Fast & Slow Pointers (Floyd's)

Fast pointer moves 2 steps per iteration, slow pointer moves 1 step. If there's a cycle, they meet inside it. If there's no cycle, fast reaches the end. This is Floyd's cycle detection algorithm โ€” O(n) time, O(1) space. Beyond cycle detection, the same technique finds the middle of a linked list (when fast reaches the end, slow is at the middle) and detects cycles in abstract sequences (like "Happy Number" LC 202).

Pseudocode โ€” Linked List Cycle Detection
function hasCycle(head): slow = head, fast = head while fast != null and fast.next != null: slow = slow.next // 1 step fast = fast.next.next // 2 steps if slow == fast: return true // met inside cycle return false // fast reached end โ†’ no cycle // O(n), O(1) space
Floyd's Cycle Detection โ€” fast catches slow inside the cycle
1 2 3 4 5 meet here! 6 slow: 1โ†’2โ†’3โ†’4โ†’5 fast: 1โ†’3โ†’5โ†’4โ†’6โ†’5 (wraps) Both at node 5 โ†’ cycle detected! Middle of list (no cycle): When fast hits end, slow is at the middle.
Find Middle Node โ€” Fast/Slow
function findMiddle(head): slow = head, fast = head while fast != null and fast.next != null: slow = slow.next fast = fast.next.next return slow // slow is at the middle
Floyd's algorithm also finds the cycle START:
  • After slow and fast meet inside the cycle, reset one pointer to head.
  • Move both at speed 1 โ€” they meet again at the cycle's entry point.
  • This solves LC 142 "Linked List Cycle II" and is provable mathematically: if the tail has length a and the cycle has length c, they meet at distance a from the cycle start.
06
Section Six ยท Merge Pattern

Pattern 4 โ€” Merge Two Sequences

Two pointers, each starting at the beginning of a different sorted sequence. Compare the two pointed elements, process the smaller one, advance that pointer. Continues until both sequences are exhausted. This is the merge step of Merge Sort, and the basis of "Merge Sorted Array" (LC 88), "Merge Two Sorted Lists" (LC 21), and "Intersection of Two Arrays" (LC 349).

Pseudocode โ€” Merge Two Sorted Arrays
function merge(A, B): i = 0, j = 0 result = [] while i < A.length and j < B.length: if A[i] <= B[j]: result.add(A[i++]) else: result.add(B[j++]) // copy remaining elements from A or B while i < A.length: result.add(A[i++]) while j < B.length: result.add(B[j++]) return result // O(n + m)
Merge in-place (LC 88 trick):
  • When merging into array A which has enough space, fill from the END (right to left) โ€” compare the largest unplaced elements and put them at the back.
  • This avoids overwriting unprocessed elements: A[k--] = A[i] > B[j] ? A[i--] : B[j--].
07
Section Seven ยท When to Use

Choosing the Right Pattern

Signal Pattern Examples
Sorted array + find pair with property Opposite ends Two Sum II, 3Sum, Container
Remove/compact elements in-place Read/write (same dir) Remove Dups, Move Zeroes
Cycle in linked list or sequence Fast/slow Cycle Detection, Happy Number
Middle of linked list Fast/slow Middle Node, Palindrome LL
Merge two sorted sequences Merge (two inputs) Merge Array, Merge Lists
Palindrome check Opposite ends Valid Palindrome
Three values (Dutch flag) Three pointers Sort Colors (LC 75)
sorted array? pair/sum problem? Yes Opposite Ends No compact in-place? linked list cycle? compact Read/Write Fast/Slow cycle / middle merge Merge Pattern
08
Section Eight ยท Implementation

Build It Once

Build each pattern once โ€” they're short enough to memorise. In interviews, the code is 5โ€“10 lines; the hard part is recognising which pattern to apply.

Java โ€” All four patterns
// 1. Opposite ends โ€” Two Sum II โ€” O(n) int[] twoSum(int[] arr, int target) { int L = 0, R = arr.length - 1; while (L < R) { int sum = arr[L] + arr[R]; if (sum == target) return new int[]{L, R}; else if (sum < target) L++; else R--; } return new int[]{-1, -1}; } // 2. Read/write โ€” Remove Duplicates โ€” O(n), O(1) int removeDuplicates(int[] arr) { if (arr.length == 0) return 0; int w = 1; for (int r = 1; r < arr.length; r++) if (arr[r] != arr[r - 1]) arr[w++] = arr[r]; return w; } // 3. Fast/slow โ€” Cycle detection โ€” O(n), O(1) boolean hasCycle(ListNode head) { ListNode slow = head, fast = head; while (fast != null && fast.next != null) { slow = slow.next; fast = fast.next.next; if (slow == fast) return true; } return false; }
09
Section Nine ยท Java Reference

Use It In Java

IN JAVA โ€” Two-Pointer Idioms
Array index int L = 0, R = arr.length - 1; โ€” most common form
ListNode ListNode slow = head, fast = head; โ€” linked list traversal
Pattern Loop condition Key idiom
Opposite ends while (L < R) L++ or R-- based on comparison
Read/write for (r = 0; r < n; r++) arr[w++] = arr[r] when condition met
Fast/slow while (fast != null && fast.next != null) slow = slow.next; fast = fast.next.next
Merge while (i < m && j < n) Take smaller, advance that pointer
3Sum outer for + inner while (L < R) Skip duplicates with while (nums[L]==nums[L+1]) L++
โš  Gotcha: In the fast/slow linked list pattern, always check fast != null && fast.next != null BEFORE accessing fast.next.next โ€” otherwise you get a NullPointerException on odd-length lists. The order of the null check matters: fast first, then fast.next.
โš  Gotcha: In 3Sum, skipping duplicates requires checking AFTER adding a result โ€” while (L < R && nums[L] == nums[L+1]) L++. Skipping before checking misses valid triplets; skipping after ensures you find at least one instance of each unique triplet.
10
Section Ten ยท Practice

Problems To Solve

Two-pointer problems test pattern recognition more than coding skill โ€” every problem is 5โ€“15 lines once you identify the right pattern. The challenge is knowing whether to use opposite ends (sorted pair), read/write (in-place compact), or fast/slow (linked list). Read the problem constraints: sorted array + pair โ†’ opposite ends. In-place + O(1) space โ†’ read/write. Linked list + cycle or middle โ†’ fast/slow.

Difficulty Pattern Problem Key Insight
Easy opposite Two Sum II โ€” LC 167 Sorted array, find pair with target sum. L at start, R at end. Sum too small โ†’ L++. Sum too big โ†’ R--. O(n).
Easy opposite Valid Palindrome โ€” LC 125 L and R converge from opposite ends, skipping non-alphanumeric characters. Compare lowercase characters at each step.
Easy read/write Move Zeroes โ€” LC 283 Write pointer compacts non-zero elements to the front. Fill remaining positions with 0. O(n), O(1).
Medium opposite 3Sum โ€” LC 15 Sort + fix one element + two-pointer on the rest. Skip duplicates at both the outer loop and inner two-pointer to avoid duplicate triplets.
Medium opposite Container With Most Water โ€” LC 11 Opposite ends. Area = width ร— min(height). Move the shorter side inward โ€” the only direction that can increase the limiting height.
Medium fast/slow Linked List Cycle II โ€” LC 142 Fast/slow to detect cycle. Then reset one to head, advance both at speed 1 โ€” they meet at the cycle entry point. Mathematical proof: tail length = remaining cycle distance.
Interview Pattern:
  • Two pointers is the most concise technique โ€” every solution is under 15 lines.
  • The interview skill is not writing the code but recognising the pattern. Sorted + pair โ†’ opposite ends.
  • In-place filter โ†’ read/write. Linked list structure โ†’ fast/slow. Once you identify the pattern, the code writes itself.

โ†’ See all Two Pointers problems with full solutions