Two Pointers
Overview
Each pointer represents an index or position within a data structure. With two pointers, we enable efficient comparisons and traversals that would otherwise require nested loops.
When to Use
- Linear data structure (arrays, strings, linked lists)
- Input follows a predictable pattern (e.g., sorted array, palindromic string)
- Problem asks for a pair or range of values
- Need to compare elements from different positions
Traversal Strategies
Inward Traversal
Pointers start at opposite ends and move toward each other.
L R
↓ ↓
[1, 2, 3, 4, 5, 6]
╰─────→ ←──────╯
- Movement: Both pointers move each iteration (one from left, one from right)
- Ideal for: Comparing elements from both ends, palindromes, sorted array pair finding
Unidirectional Traversal
Both pointers start at the same end and move in the same direction.
W R
↓ ↓
[0, 1, 0, 3, 12]
╰───────────────→
- Movement: Both pointers move forward, but at different rates or conditions
- Ideal for: In-place array modification, partitioning elements
Problems
Pair Sum – Sorted
Given an array of integers sorted in ascending order and a target value, return the indexes of any pair of numbers in the array that sum to the target. The order of the indexes in the result doesn't matter. If no pair is found, return an empty array.
Example 1:
Input: nums = [-5, -2, 3, 4, 6], target = 7
Output: [2, 3]
Explanation: nums[2] + nums[3] = 3 + 4 = 7
Example 2:
Input: nums = [1, 1, 1], target = 2
Output: [0, 1]
Explanation: other valid outputs could be [1, 0], [0, 2], [2, 0], [1, 2] or [2, 1].
Brute Force Approach
Checking all possible pairs using two nested loops. An outer loop traverses for the first number of the pair, an inner loop traverses the rest of the array for the second number of the pair.
This approach has a time complexity of O(n^2), and doesn't take into account the fact that the input is sorted.
Two Pointers Approach
For any value at left and right:
- If their sum is less than the target, move
leftright. - If their sum is greater than the target, move
rightleft. - If their sum is equal to the target, return
[left, right].
Complexity:
- Time complexity: O(n).
- Space complexity: O(1).
Interviewing Tips
- Consider all information provided. The key insight is that the input is sorted.
Triplet Sum
Given an array of integers, return all triplets [a, b, c] (values, not indexes) such that a + b + c = 0. The
solution must not contain duplicate triplets (e.g., [1, 2, 3] and [2, 3, 1] are considered duplicates). If no such
triplets are found, return an empty array.
Example 1:
Input: nums = [-1, 0, 1, 2, -1, -4]
Output: [[-1, -1, 2], [-1, 0, 1]]
Explanation: nums[0] + nums[2] + nums[3] = -1 + 1 + 2 = 0 and nums[0] + nums[1] + nums[4] = -1 + 0 + 1 = 0.
Example 2:
Input: nums = [0, 0, 0, 0]
Output: [[0, 0, 0]]
Explanation: Only one unique triplet sums to zero, despite multiple valid index combinations.
Brute Force Approach
Check all possible triplets using three nested loops. For each combination of three elements, check if their sum equals zero.
This approach has a time complexity of O(n³) and doesn't leverage sorting to eliminate duplicates efficiently.
Two Pointers Approach
Sort the array first, then fix one element a and use pair sum to find b + c = -a from the remaining elements:
- For each
a, setleftto the element afteraandrightto the end. - If
a + left + right < 0, moveleftright. - If
a + left + right > 0, moverightleft. - If
a + left + right = 0, record the triplet and move both pointers.
Optimizations:
- Skip duplicate values of
ato avoid duplicate triplets. - Skip duplicate values of
borcwithin the pair sum. - Stop when
abecomes positive (triplets summing to 0 cannot be formed with all positive numbers).
Complexity:
- Time complexity: O(n²) — Sort costs O(n log n), then for each element, pair sum costs O(n).
- Space complexity: O(n) for sorting (depends on implementation).
Interviewing Tips
- Clarify whether the output should contain values or indexes.
- Ask about handling duplicates in both input and output.
Is Palindrome Valid
Given a string, determine if it's a palindrome after removing all non-alphanumeric characters. A character is alphanumeric if it's either a letter or a number.
Example 1:
Input: s = "a dog! a panic in a pagoda"
Output: true
Explanation: After removing non-alphanumeric characters: "adogapanicainapagoda", which reads the same forwards and
backwards.
Example 2:
Input: s = "abc123"
Output: false
Explanation: After removing non-alphanumeric characters: "abc123", which is not a palindrome.
Brute Force Approach
Create a new string with only alphanumeric characters (lowercased), then compare it with its reverse.
This approach has a time complexity of O(n) but uses O(n) extra space for the filtered string and its reverse.
Two Pointers Approach
A palindrome is identical when read left to right and right to left. Use inward traversal, skipping non-alphanumeric characters:
- Start
leftat the beginning,rightat the end. - Skip non-alphanumeric characters by moving each pointer inward.
- Compare characters (case-insensitive). If they differ, return
false. - Move both pointers inward and repeat until they meet.
Complexity:
- Time complexity: O(n).
- Space complexity: O(1).
Interviewing Tips
- Clarify constraints: presence of non-alphanumeric characters, their treatment, role of numbers, case sensitivity.
- Confirm before using significant built-in functions (e.g.,
isalnum, regex).
Largest Container
You are given an array of numbers, each representing the height of a vertical line on a graph. A container can be formed with any pair of these lines, along with the x-axis of the graph. Return the amount of water the largest container can hold.
Example 1:
Input: heights = [2, 7, 8, 3, 7, 6]
Output: 24
Explanation: The container formed between index 1 (height 7) and index 4 (height 7) has width 3 and height 7,
giving min(7, 7) * (4 - 1) = 21. The optimal container is between index 1 and index 5: min(7, 6) * 4 = 24.
Example 2:
Input: heights = [1, 1]
Output: 1
Explanation: The only container possible has width 1 and height 1.
Brute Force Approach
Check all possible pairs of lines and calculate the water each container can hold. Track the maximum.
This approach has a time complexity of O(n²).
Two Pointers Approach
For two vertical lines, the water contained is min(heights[i], heights[j]) * (j - i). The minimum height is used
because water above it would overflow.
- Start
leftat the beginning,rightat the end. - Calculate water for current container and update maximum.
- Move the pointer pointing to the shorter line inward.
- Repeat until pointers meet.
Why move the shorter line? Moving the taller line can only decrease or maintain the width while the height is still limited by the shorter line. Moving the shorter line gives a chance to find a taller line.
Complexity:
- Time complexity: O(n).
- Space complexity: O(1).
Interviewing Tips
- Clarify whether heights can be zero or negative.
- Explain the greedy insight: moving the shorter line is optimal because keeping it limits potential area.
Shift Zeros to End
Given an array of integers, modify the array in place to move all zeros to the end while maintaining the relative order of non-zero elements.
Example 1:
Input: nums = [0, 1, 0, 3, 12]
Output: [1, 3, 12, 0, 0]
Explanation: All zeros are moved to the end while 1, 3, 12 maintain their relative order.
Example 2:
Input: nums = [0, 0, 1]
Output: [1, 0, 0]
Explanation: The single non-zero element moves to the front.
Brute Force Approach
Build a new array by first collecting all non-zero elements, then appending zeros to fill the remaining positions. Copy the result back to the original array.
This approach has a time complexity of O(n) but uses O(n) extra space.
Two Pointers Approach
Use the reader-writer pattern (unidirectional traversal). The reader scans through the array, while writer tracks
where the next non-zero should go:
- Both pointers start at the beginning.
- When
readerfinds a non-zero, swap it with the element atwriter, then advancewriter. - Always advance
reader.
Complexity:
- Time complexity: O(n).
- Space complexity: O(1).
Interviewing Tips
- Clarify whether in-place modification is required or if extra space is allowed.
- Ask about the expected ratio of zeros to non-zeros (affects number of swaps).
Next Lexicographical Sequence
Given a string of lowercase English letters, rearrange the characters to form a new string representing the next immediate sequence in lexicographical (alphabetical) order. If the given string is already last in lexicographical order among all possible arrangements, return the arrangement that's first in lexicographical order.
Example 1:
Input: s = "abcd"
Output: "abdc"
Explanation: The next permutation after "abcd" swaps c and d.
Example 2:
Input: s = "dcba"
Output: "abcd"
Explanation: "dcba" is the last permutation, so we wrap around to the first: "abcd".
Brute Force Approach
Generate all permutations of the string, sort them lexicographically, find the current string, and return the next one (or the first if current is last).
This approach has a time complexity of O(n! × n) which is impractical for any reasonable input size.
Two Pointers Approach
This problem uses multiple pointer passes to find the next permutation in O(n) time:
- Find the pivot: Scan from right to find the rightmost character that breaks the non-increasing sequence.
- Handle edge case: If no pivot found, the string is already the last sequence — reverse and return.
- Find successor: Find the rightmost character greater than the pivot.
- Swap: Swap the pivot with its successor to increase lexicographical order.
- Minimize suffix: Use two pointers to reverse the suffix after the pivot (making it the smallest permutation).
Complexity:
- Time complexity: O(n).
- Space complexity: O(n) for the character array, O(1) if modifying in place.
Interviewing Tips
- Be precise with terminology: it's "non-increasing" (allows equal), not "decreasing" (strictly less).
- Walk through an example step-by-step to demonstrate understanding of the algorithm.