The problem
You get an array numbers sorted in non-decreasing order and a target. Exactly one pair adds up to the target. Return the two positions as 1-indexed [i, j] with i < j. You must use only constant extra space.
Input: numbers = [1, 3, 4, 5, 7, 11], target = 9
Output: [3, 4]4 + 5 = 9, and they sit at 1-indexed positions 3 and 4.
How to think about it
Start with brute force. Try every pair with two nested loops: O(n²). The classic Two Sum trick, a hash map of complements, gets O(n) time but uses O(n) space, which this version forbids.
The key insight: sorting tells you which way to move. Put i at the smallest number and j at the largest, and look at their sum. If it’s too small, the only way to increase it is to move i right, since moving j left would only make it smaller. If it’s too big, move j left. Each comparison throws away one candidate for good.
Why you can’t skip the answer. Suppose the sum is too small. Then numbers[i] paired with any j' ≤ j is also too small, because numbers[j'] is at most numbers[j]. So numbers[i] can’t be part of the answer, and dropping it is safe. The same argument works in reverse for j. That’s the proof interviewers like to hear.
Which pattern is this? Converging two pointers. The tell is “sorted array, find a pair with a sum (or difference) condition”.
Approach
- Set
i = 0andj = n - 1. - While
i < j, computes = numbers[i] + numbers[j]. - If
s == target, return[i + 1, j + 1]. - If
s < target, moveiright; otherwise movejleft. - If the pointers meet, there’s no pair (not possible under the problem’s guarantee).
Solution
def two_sum_sorted(nums, target):
i, j = 0, len(nums) - 1
while i < j:
s = nums[i] + nums[j]
if s == target: return [i+1, j+1] # 1-indexed answer
if s < target: i += 1 # need bigger: drop the smallest
else: j -= 1 # need smaller: drop the largest
return []
# two_sum_sorted([2,7,11,15], 9) -> [1,2]Dry run
On numbers = [1, 3, 4, 5, 7, 11], target = 9:
i |
j |
nums[i] + nums[j] |
Decision |
|---|---|---|---|
| 0 | 5 | 1 + 11 = 12 | too big, j -= 1 |
| 0 | 4 | 1 + 7 = 8 | too small, i += 1 |
| 1 | 4 | 3 + 7 = 10 | too big, j -= 1 |
| 1 | 3 | 3 + 5 = 8 | too small, i += 1 |
| 2 | 3 | 4 + 5 = 9 | found |
The function returns [3, 4].
Complexity
- Time: O(n). Every step moves one pointer inward, so there are at most n − 1 steps.
- Space: O(1). Two indices and a sum.
Edge cases and pitfalls
- 1-indexed output. Returning
[i, j]instead of[i+1, j+1]is the most common bug. - Duplicates like
[3, 3]with target 6 work fine:i = 0,j = 1. - Negative numbers don’t change anything; the sort order is all that matters.
- Using the same element twice is prevented by
i < j. - Reaching for a hash map solves it but ignores the sorted input and breaks the O(1) space requirement.
Related problems
- Two Sum (LeetCode 1): the unsorted version, where a hash map of complements is the right tool.
- 3Sum (LeetCode 15): fix one number, then run exactly this two-pointer scan on the rest.
- Container With Most Water (LeetCode 11): another “move the pointer that can’t be part of a better answer” argument.
- 1.Valid Palindrome (LeetCode 125)
- 2.Remove Duplicates from Sorted Array (LeetCode 26)
- 3.Move Zeroes (LeetCode 283)
- 4.Squares of a Sorted Array (LeetCode 977)
- 5.Backspace String Compare (LeetCode 844)
- 6.Two Sum II – Input Array Is Sorted (LeetCode 167)
- 7.3Sum (LeetCode 15)
- 8.3Sum Closest (LeetCode 16)
- 9.4Sum (LeetCode 18)
- 10.Container With Most Water (LeetCode 11)
- 11.Sort Colors (LeetCode 75)
- 12.Trapping Rain Water (LeetCode 42)