Two pointers
Walk two indexes through an array, from both ends or in the same direction, to solve pair, partition and in-place problems in O(n) with no extra memory.
Opposite ends: one pointer starts at the left, one at the right, and they move towards each other. Used on sorted data and for reversing or mirroring. Same direction (often called slow and fast, or read and write): both start on the left; one scans every item, the other moves only when something is kept.
Opposite ends: a pair with a given sum
static (int, int)? PairWithSum(int[] sorted, int target){ int left = 0, right = sorted.Length - 1; while (left < right) { int sum = sorted[left] + sorted[right]; if (sum == target) return (left, right); if (sum < target) left++; // need a bigger sum else right--; // need a smaller sum } return null;} int[] a = { 1, 3, 4, 6, 8, 9, 11, 14 };Console.WriteLine(PairWithSum(a, 13));Console.WriteLine(PairWithSum(a, 21)?.ToString() ?? "none");(2, 5) none
Same direction: remove repeats in place
static int RemoveDuplicates(int[] a){ if (a.Length == 0) return 0; int write = 1; // a[0] is always kept for (int read = 1; read < a.Length; read++) if (a[read] != a[write - 1]) // a new value a[write++] = a[read]; return write; // number of values kept} int[] spans = { 2, 2, 3, 3, 3, 5, 8, 8, 9 };int n = RemoveDuplicates(spans);Console.WriteLine(n);Console.WriteLine(string.Join(", ", spans[..n]));5 2, 3, 5, 8, 9
List<T>.RemoveAll works internally, which is why RemoveAll is O(n) while calling Remove in a loop is O(n²).
Opposite ends: mirroring
static bool IsPalindrome(string s){ int i = 0, j = s.Length - 1; while (i < j) { if (!char.IsLetterOrDigit(s[i])) { i++; continue; } if (!char.IsLetterOrDigit(s[j])) { j--; continue; } if (char.ToLowerInvariant(s[i]) != char.ToLowerInvariant(s[j])) return false; i++; j--; } return true;} Console.WriteLine(IsPalindrome("Never odd or even"));Console.WriteLine(IsPalindrome("Steel beam"));True False
When does two pointers apply?
| Clue in the problem | Shape |
|---|---|
{ -7, -3, 2, 3, 11 } the answer is 4, 9, 9, 49, 121. Do it in O(n), without sorting.
Show a hint
Show the solution
static int[] SortedSquares(int[] a){ var result = new int[a.Length]; int left = 0, right = a.Length - 1; for (int k = a.Length - 1; k >= 0; k--) { int l = a[left] * a[left], r = a[right] * a[right]; if (l > r) { result[k] = l; left++; } else { result[k] = r; right--; } } return result;} Console.WriteLine(string.Join(", ", SortedSquares(new[] { -7, -3, 2, 3, 11 })));4, 9, 9, 49, 121
Remember
Two indexes that only move forward (or inward) give O(n) time and O(1) extra memory. Opposite ends on sorted data: move the pointer that can no longer be part of an answer. Read/write pointers filter or remove repeats in place; this is how RemoveAllis O(n).Compared with a hash table, two pointers saves memory but usually needs sorted data.