Two Sum II - Input Array Is Sorted
Difficulty: Medium
You're given a list of numbers that is already sorted in increasing order, and a target number. Find two different numbers in the list that add up exactly to the target, and return their positions.
There's a small twist compared to the usual Two Sum: positions here are counted starting from 1, not 0. You can assume there's always exactly one valid pair, you can't reuse the same element twice, and — since the list is already sorted — you should be able to solve this using only a constant amount of extra space.
Examples
Input: numbers = [2, 7, 11, 15], target = 9
Output: [1, 2]
numbers[0] + numbers[1] = 2 + 7 = 9, and using 1-based positions that's [1, 2].
Input: numbers = [2, 3, 4], target = 6
Output: [1, 3]
2 + 4 = 6, which are positions 1 and 3.
Input: numbers = [-1, 0], target = -1
Output: [1, 2]
Constraints
2 <= numbers.length <= 3 * 10^4
-1000 <= numbers[i] <= 1000
numbers is sorted in non-decreasing order.
Exactly one valid answer exists.
Approach
Because the list is sorted, you get a useful guarantee: moving the left pointer forward can only increase the sum, and moving the right pointer backward can only decrease it. That means you never have to backtrack — you always know exactly which pointer to move next.
Start with one pointer at the very first number and one at the very last. If their sum is too small, the only way to increase it is to move the left pointer forward (to a bigger number). If it's too big, move the right pointer backward (to a smaller number). If it matches, you're done. This finds the answer in a single pass, using no extra memory beyond the two pointers.
Solutions
Brute Force — Check Every Pair
Try every possible pair of positions and check whether they sum to the target. Correct, but it doesn't use the fact that the list is sorted at all.
function twoSum(numbers, target) {
for (let i = 0; i < numbers.length; i++) {
for (let j = i + 1; j < numbers.length; j++) {
if (numbers[i] + numbers[j] === target) {
return [i + 1, j + 1];
}
}
}
return [];
}Time: O(n²) · Space: O(1)
Hash Map
Walk through the list once, and for each number check whether the value needed to complete the pair has already been seen, using a Map for instant lookups. This ignores the sorted order and uses O(n) extra memory, but it's faster than the brute force.
function twoSum(numbers, target) {
const seen = new Map(); // value -> index
for (let i = 0; i < numbers.length; i++) {
const complement = target - numbers[i];
if (seen.has(complement)) {
return [seen.get(complement) + 1, i + 1];
}
seen.set(numbers[i], i);
}
return [];
}Time: O(n) · Space: O(n)
Optimal — Two Pointers
Take advantage of the sorted order directly. Start one pointer at each end of the list, and move whichever pointer will move the sum in the right direction: forward from the left if the sum is too small, backward from the right if it's too big.
function twoSum(numbers, target) {
let left = 0;
let right = numbers.length - 1;
while (left < right) {
const sum = numbers[left] + numbers[right];
if (sum === target) {
return [left + 1, right + 1];
} else if (sum < target) {
left++;
} else {
right--;
}
}
return [];
}Time: O(n) · Space: O(1)