#167MediumTwo pointers
Two Sum II - Input Array Is Sorted
Start dumb: try every pair
Two positions, one target sum — the honest first idea is the double loop:
for every i, try every j > i and test numbers[i] + numbers[j] == target.
That’s O(n²) comparisons, and it treats the input as an arbitrary bag of
numbers.
Find the bottleneck
The brute force ignores the one gift this problem hands you: the array is sorted. Sortedness makes the pair sum predictable — you always know which direction a pointer move pushes it. Testing pairs in arbitrary order throws that signal away and re-learns nothing from each failed comparison.
The optimal shape
Two pointers converge from the ends of the array:
- Compute the sum of the two pointed-at values.
- Exact hit → done (report 1-based positions).
- Too small → only moving
leftrightward can raise the sum. - Too big → only moving
rightleftward can lower it.
Each step retires one value, so the pointers cross after at most n − 1
moves — O(n) time and, unlike the hash-map Two Sum, O(1) space:
sortedness replaces the lookup table. Watch the window between the
pointers in the player below — it only ever shrinks, never re-expands.
That one-way collapse is the proof.
Watch it think
Step through the default input, then probe the edges: duplicates
(3,3 | 6), negatives (-3,3,4 | 0), an impossible target
(1,2,3 | 100), or an unsorted list — the player will tell you why
sortedness is non-negotiable.
Re-traces as you type — comma-separated non-decreasing integers (2–14), a pipe, then the target — e.g. "2,7,11,15 | 9". Capped at 32 characters so the tiles stay readable.
Step 1 of 7: The two ends form the widest pair — every other pair sits strictly inside it, so nothing has been ruled out yet. Variables: left = 0, right = 3, sum = —, target = 9.
Data
Code
1fun twoSum(numbers: IntArray, target: Int): IntArray {2 var left = 03 var right = numbers.lastIndex4 while (left < right) {5 val sum = numbers[left] + numbers[right]6 when {7 sum == target -> return intArrayOf(left + 1, right + 1)8 sum < target -> left++9 else -> right--10 }11 }12 return intArrayOf(-1, -1)13}
State
- left
- 0
- right
- 3
- sum
- —
- target
- 9
Why this step
The two ends form the widest pair — every other pair sits strictly inside it, so nothing has been ruled out yet.
1 / 7
The same pattern also solves
Recognize the window, and these fall to the same skeleton.
- Two Sum (LC 1) ↗
The "what if it weren't sorted?" pivot — without sorted order you fall back to the O(n) hash map and pay O(n) space for the lookup table.
- 3Sum (LC 15) ↗
Fix one element, then run this exact converging-pointer sweep on the remainder — the natural escalation to a harder variant.
- 3Sum Closest (LC 16) ↗
Same sweep, but instead of stopping on an exact hit you track the nearest sum seen so far.
- 4Sum (LC 18) ↗
Fix two elements with nested loops and the innermost layer is still this two-pointer skeleton.