You learned how two pointers finds pairs on sorted arrays. Start at both ends, compare the sum to the target, squeeze inward. Clean and fast.
But here is the thing most tutorials skip: most arrays are not sorted. And when a candidate reaches for two pointers on unsorted data in an interview, something bad happens. Not slow. Wrong.
Take [7, 3, 11, 1, 9, 5] and target 10. There is a valid pair in there: 7 + 3. Let's run two pointers on it and see what happens.
Think about it for a second before you step through. Two pointers starts at both ends and moves inward based on the sum. On a sorted array, “sum too small, move left pointer right” always increases the sum, because every element to the right is larger. What happens when the array is not sorted? Does that guarantee still hold?
That question is what this entire module is about. The answer is not theoretical. It is viscerally wrong, and you are about to watch it happen.
Step through the algorithm on this unsorted array. At each step, the pointers follow the same rules you learned: move L right if the sum is too small, move R left if it is too big.
Pay attention to what happens at the end. The algorithm will terminate. The question is: with what answer?
L=7, R=5. The array 7311195 is unsorted. Will two-pointer find target 10?
function twoSum(nums, target) { let left = 0, right = nums.length - 1; while (left < right) { const sum = nums[left] + nums[right]; if (sum === target) return [left, right]; if (sum < target) left++; else right--; } return null;}You just watched the algorithm say “no pair found” while 7 + 3 = 10 was sitting right there -- literally the first two elements.
Sit with that for a second. This is not a performance problem. The algorithm did not find a slow answer. It found a wrong answer. It told you the pair does not exist when it does. In an interview, that is a silent bug that passes no test cases.
Think back to the moment it went wrong. The algorithm moved L rightward because the sum was “too small.” On a sorted array, that move guarantees a larger sum -- every value to the right is bigger. That is not a heuristic. It is a mathematical invariant that sorted order provides.
But on this unsorted array? Moving L right might give you a smaller value, a bigger one, the same one -- anything. The algorithm made directional decisions based on a property the data did not have. The mechanism worked, but nothing connected it to reality.
Imagine you are searching for a word in a dictionary. You open to the middle and see “mango.” You are looking for “python.” Since dictionaries are alphabetical, you know to flip forward. The M-section is behind you. You can discard the entire first half in one move.
Now imagine the dictionary is not alphabetical. The pages are shuffled randomly. You open to the middle and see “mango.” You are looking for “python.” Which direction do you flip? Forward? “Python” could be on the previous page. Backward? It could be on the next one. The information you gained from seeing “mango” tells you nothing about where “python” lives.
That is exactly what happens to two pointers on unsorted data. Each comparison gives you information only if the data is ordered. Without order, you are flipping pages at random and hoping for the best.
Two pointers is not just “start at both ends and move inward.” It is a search that works because sorted order creates a monotonic relationship between pointer movement and sum change:
L right: sum can only increase (or stay the same with duplicates)R left: sum can only decrease (or stay the same with duplicates)Toggle between sorted and shuffled to feel the difference. On sorted data, moving L right always increases the sum -- predictable, controllable. On shuffled data, the same move produces a wildly different delta. That is the betrayal.
This monotonicity is the engine. Without it, each move is a coin flip. The algorithm might stumble onto the right answer by luck, but it has no systematic way to find it. And when it misses, it does not know it missed. It terminates confidently with a wrong answer.
That confidence is what makes this bug dangerous. The code does not crash. It does not throw an error. It returns a clean, well-formatted, incorrect result.
Same six numbers, same target. But this time, sort the array before running two pointers.
Watch how the same logic that failed moments ago now works perfectly. The algorithm has not changed. The only difference is that the data satisfies its precondition.
Two-pointer failed on 7311195. If we sort it to 1357911, will it find target 10?
Sorting fixed the correctness issue. But it created a new one.
Some problems do not just ask for the values. They ask for the original positions. “Return the indices of the two numbers that add up to the target.” If you sort first, what happens to those positions?
This is not an edge case. LeetCode 1 (Two Sum), the most-solved problem on the platform, requires original indices. If you sort the array, you need a separate mapping from sorted positions back to original positions, and at that point you have built most of a hash map anyway.
The rule is simple: if the output references positions in the original array, sorting is either off the table or requires bookkeeping that defeats the purpose. When the output only references values (or when the array is already sorted), two pointers is the cleaner tool.
This creates a natural decision tree that you will build intuition for over the next few screens.
You have three tools in your pocket for pair-sum problems, and they are not interchangeable. Each one trades off differently on time, space, and what information it preserves.
For each scenario below, the constraints tell you which tradeoff matters. Pick the approach that fits. The point is not to memorize: it is to develop the instinct for reading the constraints and letting them guide you.
You have seen all three approaches work. But in an interview, “it works” is table stakes. The follow-up question is always: "Why this approach and not the other one?"
That question is about tradeoffs, not correctness. And the tradeoffs are more subtle than “faster is better.” The right answer depends on what you are willing to spend and what you need to preserve.
Think of the three approaches as points on a spectrum:
Brute force sits at one extreme: no extra space, no preconditions, but O(n^2) time. It is the fallback when nothing else works, and it is the baseline you are trying to beat.
Hash map sits at the other extreme: O(n) time, works on any array, preserves indices, but costs O(n) space. It is the most flexible tool, but flexibility has a price.
Sort + two pointers sits in the middle: O(n log n) time, O(1) extra space if you can sort in-place, but requires (or creates) sorted order and destroys original positions. It is the space-efficient choice when you do not need indices.
The interview question is never “which is best?” It is "which is best given these constraints?" And the constraints always point to one answer:
One wrinkle worth flagging: duplicates. Consider [3, 3, 4, 4] with target 6. Two pointers handles this naturally: L starts at index 0 (3), R starts at index 3 (4). Sum is 7, too high, so R moves left to index 2 (4). Sum is still 7, so R moves to index 1 (3). Now sum is 6. Found it.
But what if you need all pairs that sum to the target? Now duplicates create multiple valid pairs, and you need to decide: does the problem want unique value pairs or all index pairs? Two pointers can skip duplicates efficiently (advance the pointer while the value does not change), but it cannot report original indices. Hash maps track exact positions, so they handle the “all index pairs” case naturally.
The decision tree still works. The question “do I need indices?” just becomes even more important when duplicates are involved.
Someone wrote this in an interview. It looks like clean two-pointer code. The logic is correct, the pointer movements are right, the termination condition is sound. But there is a problem, and it is not in the logic.
Run the code, see it break, then tap the line where the fix belongs.
Four blanks. The first one is the line that this entire module exists to teach: the precondition that makes everything else work.
Every blank has distractors that look plausible if you are not thinking about what two pointers actually needs. Reverse the array? Filter falsy values? Off-by-one on the right pointer? These are the mistakes that happen when you treat two pointers as a recipe instead of understanding why each line exists.
Three interview scenarios. Different constraints, different right answers. These are modeled on real problems: classic LeetCode, a variant with different constraints, and a system design curveball.
For each one, read the constraints carefully. The approach you pick should follow directly from the constraints, not from habit.