167. Two Sum II - Input Array Is Sorted
Problem Description
You are given a 1-indexed array of integers called numbers that is already sorted in non-decreasing order (smallest to largest). Your task is to find two numbers in this array that add up to a specific target value.
The key requirements are:
- The array uses 1-based indexing (the first element is at index 1, not 0)
- You need to find exactly two different elements whose sum equals the target
- Return the indices of these two numbers as
[index1, index2]whereindex1 < index2 - Since the array is 1-indexed, you need to add 1 to the actual array positions
- You cannot use the same element twice (the two numbers must be at different positions)
- The problem guarantees exactly one solution exists
- Your solution must use only constant extra space (O(1) space complexity)
For example, if numbers = [2, 7, 11, 15] and target = 9, you would return [1, 2] because numbers[1] + numbers[2] = 2 + 7 = 9.
The solution leverages the sorted nature of the array. For each element numbers[i], it uses binary search to look for the complement value target - numbers[i] in the remaining portion of the array (from position i+1 onwards). The bisect_left function efficiently finds where the complement would be positioned in the sorted array. If the complement exists at that position, the function returns the 1-indexed positions of both numbers.
Intuition
When we need to find two numbers that sum to a target, the most straightforward approach would be to check every possible pair, but that would take O(n²) time. However, we can take advantage of the fact that the array is already sorted.
The key insight is that for any number in our array, we know exactly what its complement needs to be: complement = target - current_number. Since the array is sorted, we can efficiently search for this complement using binary search instead of checking every element.
Here's the thought process:
- Pick a number from the array (let's call it
numbers[i]) - Calculate what value we need to find:
complement = target - numbers[i] - Since the array is sorted, we can use binary search to quickly check if
complementexists in the remaining part of the array
Why search only in the remaining part (from i+1 onwards)? Because:
- We can't use the same element twice
- If the complement existed before position
i, we would have already found it when we processed earlier elements - This prevents us from finding duplicate pairs like
[i, j]and[j, i]
The feasible function: For each position j in the search range, we ask: "Is numbers[j] >= complement?" This creates a pattern of [false, false, ..., true, true, ...] in a sorted array. We want to find the first position where this becomes true, and then check if the value there equals exactly complement.
Binary search reduces the search time from O(n) to O(log n) for each element we check. Since we might need to check up to n elements in the worst case, the overall time complexity becomes O(n log n), which is much better than the brute force O(n²) approach.
Solution Implementation
1from typing import List
2
3
4class Solution:
5 def twoSum(self, numbers: List[int], target: int) -> List[int]:
6 """
7 Find two numbers in a sorted array that sum to the target.
8
9 Args:
10 numbers: Sorted array of integers in ascending order
11 target: Target sum to find
12
13 Returns:
14 List containing 1-indexed positions of the two numbers
15 """
16 n = len(numbers)
17
18 # Iterate through each number as the first element
19 for i in range(n - 1):
20 # Calculate the complement needed to reach the target
21 complement = target - numbers[i]
22
23 # Binary search template to find first index where numbers[mid] >= complement
24 left, right = i + 1, n - 1
25 first_true_index = -1
26
27 while left <= right:
28 mid = (left + right) // 2
29 if numbers[mid] >= complement:
30 first_true_index = mid
31 right = mid - 1
32 else:
33 left = mid + 1
34
35 # Check if the complement exists at the found position
36 if first_true_index != -1 and numbers[first_true_index] == complement:
37 # Return 1-indexed positions
38 return [i + 1, first_true_index + 1]
39
40 return [] # Problem guarantees a solution exists
411class Solution {
2 /**
3 * Finds two numbers in a sorted array that add up to the target sum.
4 * Uses binary search template to find the complement of each number.
5 *
6 * @param numbers The sorted array of integers
7 * @param target The target sum to find
8 * @return An array containing the 1-indexed positions of the two numbers
9 */
10 public int[] twoSum(int[] numbers, int target) {
11 int n = numbers.length;
12
13 // Iterate through each number as the first element of the pair
14 for (int i = 0; i < n - 1; i++) {
15 // Calculate the complement needed to reach the target
16 int complement = target - numbers[i];
17
18 // Binary search template to find first index where numbers[mid] >= complement
19 int left = i + 1;
20 int right = n - 1;
21 int firstTrueIndex = -1;
22
23 while (left <= right) {
24 int mid = left + (right - left) / 2;
25 if (numbers[mid] >= complement) {
26 firstTrueIndex = mid;
27 right = mid - 1;
28 } else {
29 left = mid + 1;
30 }
31 }
32
33 // Check if the complement exists at the found position
34 if (firstTrueIndex != -1 && numbers[firstTrueIndex] == complement) {
35 // Return 1-indexed positions
36 return new int[] {i + 1, firstTrueIndex + 1};
37 }
38 }
39
40 return new int[] {}; // Problem guarantees a solution exists
41 }
42}
431class Solution {
2public:
3 vector<int> twoSum(vector<int>& numbers, int target) {
4 int n = numbers.size();
5
6 // Iterate through each element as the first number
7 for (int i = 0; i < n - 1; ++i) {
8 // Calculate the complement needed to reach the target
9 int complement = target - numbers[i];
10
11 // Binary search template to find first index where numbers[mid] >= complement
12 int left = i + 1;
13 int right = n - 1;
14 int firstTrueIndex = -1;
15
16 while (left <= right) {
17 int mid = left + (right - left) / 2;
18 if (numbers[mid] >= complement) {
19 firstTrueIndex = mid;
20 right = mid - 1;
21 } else {
22 left = mid + 1;
23 }
24 }
25
26 // Check if the complement exists at the found position
27 if (firstTrueIndex != -1 && numbers[firstTrueIndex] == complement) {
28 // Return 1-indexed positions as required by the problem
29 return {i + 1, firstTrueIndex + 1};
30 }
31 }
32
33 // Problem guarantees exactly one solution exists
34 return {};
35 }
36};
371/**
2 * Finds two numbers in a sorted array that sum to the target value
3 * @param numbers - Sorted array of numbers in ascending order
4 * @param target - Target sum to find
5 * @returns Array containing 1-indexed positions of the two numbers
6 */
7function twoSum(numbers: number[], target: number): number[] {
8 const n = numbers.length;
9
10 // Iterate through each number as the first element
11 for (let i = 0; i < n - 1; i++) {
12 // Calculate the complement needed to reach the target
13 const complement = target - numbers[i];
14
15 // Binary search template to find first index where numbers[mid] >= complement
16 let left = i + 1;
17 let right = n - 1;
18 let firstTrueIndex = -1;
19
20 while (left <= right) {
21 const mid = Math.floor((left + right) / 2);
22 if (numbers[mid] >= complement) {
23 firstTrueIndex = mid;
24 right = mid - 1;
25 } else {
26 left = mid + 1;
27 }
28 }
29
30 // Check if the complement exists at the found position
31 if (firstTrueIndex !== -1 && numbers[firstTrueIndex] === complement) {
32 // Return 1-indexed positions as per problem requirements
33 return [i + 1, firstTrueIndex + 1];
34 }
35 }
36
37 return []; // Problem guarantees a solution exists
38}
39Solution Approach
The solution implements a binary search strategy to find the two numbers that sum to the target. We use the standard binary search template with first_true_index to find the complement.
Algorithm Overview: The approach iterates through each element in the array and uses binary search to find its complement.
Binary Search Template:
For each element numbers[i], we search for complement = target - numbers[i] using the template:
left, right = i + 1, n - 1 first_true_index = -1 while left <= right: mid = (left + right) // 2 if numbers[mid] >= complement: # feasible condition first_true_index = mid right = mid - 1 else: left = mid + 1
Step-by-Step Implementation:
-
Iterate through the array: We loop through indices from
0ton-2(wherenis the length of the array). We don't need to check the last element because it would have no elements after it to pair with. -
Calculate the complement: For each
numbers[i], we calculate the required complement:complement = target - numbers[i] -
Binary search with template: We use the boundary-finding template:
- Initialize
left = i + 1,right = n - 1, andfirst_true_index = -1 - The feasible condition is
numbers[mid] >= complement - When feasible, save
first_true_index = midand search left withright = mid - 1 - When not feasible, search right with
left = mid + 1
- Initialize
-
Check if complement exists: After binary search:
if first_true_index != -1 and numbers[first_true_index] == complement:first_true_index != -1: Ensures we found a valid positionnumbers[first_true_index] == complement: Confirms the value equals complement (not just >= complement)
-
Return the result: Return 1-indexed positions
[i + 1, first_true_index + 1]
Time Complexity: O(n log n) - We iterate through n elements, performing O(log n) binary search each. Space Complexity: O(1) - Only constant extra space used.
Ready to land your dream job?
Unlock your dream job with a 5-minute evaluator for a personalized learning plan!
Start EvaluatorExample Walkthrough
Let's walk through a concrete example to understand how the binary search template works.
Example: numbers = [1, 3, 4, 7, 11], target = 10
We need to find two numbers that sum to 10. The answer should be [2, 4] because numbers[2] + numbers[4] = 3 + 7 = 10 (using 1-based indexing).
Step-by-step execution:
Iteration 1: i = 0 (examining numbers[0] = 1)
- Calculate complement:
complement = 10 - 1 = 9 - Binary search for first index where
numbers[mid] >= 9in range [1, 4]:left = 1, right = 4, first_true_index = -1mid = 2:numbers[2] = 4 < 9→left = 3mid = 3:numbers[3] = 7 < 9→left = 4mid = 4:numbers[4] = 11 >= 9→first_true_index = 4,right = 3left > right, exit loop
- Check:
first_true_index = 4, butnumbers[4] = 11 ≠ 9 - No match found, continue
Iteration 2: i = 1 (examining numbers[1] = 3)
- Calculate complement:
complement = 10 - 3 = 7 - Binary search for first index where
numbers[mid] >= 7in range [2, 4]:left = 2, right = 4, first_true_index = -1mid = 3:numbers[3] = 7 >= 7→first_true_index = 3,right = 2mid = 2:numbers[2] = 4 < 7→left = 3left > right, exit loop
- Check:
first_true_index = 3, andnumbers[3] = 7 == 7✓ - Match found! Return
[i + 1, first_true_index + 1] = [2, 4]
Visual representation of Iteration 2:
Array: [1, 3, 4, 7, 11] Indices: 0 1 2 3 4 ↑ i = 1 numbers[i] = 3 Search for first index where value >= 7 in range [2, 4] Template finds first_true_index = 3 (where numbers[3] = 7) Verify: numbers[3] == complement (7 == 7) ✓ Return [1+1, 3+1] = [2, 4] (converting to 1-based indexing)
The algorithm successfully finds that elements at 1-based positions 2 and 4 (values 3 and 7) sum to our target of 10.
Time and Space Complexity
The time complexity is O(n × log n), where n is the length of the array numbers. This is because:
- The outer loop runs
n - 1times, iterating through each element as a potential first number - For each iteration,
bisect_leftperforms a binary search on the remaining portion of the array (from indexi + 1to the end), which takesO(log n)time - Therefore, the overall time complexity is
O(n) × O(log n) = O(n × log n)
The space complexity is O(1). The algorithm only uses a constant amount of extra space for variables i, j, x, and n, regardless of the input size. The bisect_left function performs binary search iteratively without using additional space proportional to the input.
Common Pitfalls
1. Using the Wrong Binary Search Template Variant
A common mistake is using while left < right with right = mid instead of the correct template.
Incorrect approach:
# WRONG: Using incorrect template while left < right: mid = (left + right) // 2 if numbers[mid] >= complement: right = mid else: left = mid + 1 return left # Returns insertion point, not necessarily the answer
Solution: Use the correct template with first_true_index:
first_true_index = -1 while left <= right: mid = (left + right) // 2 if numbers[mid] >= complement: first_true_index = mid right = mid - 1 else: left = mid + 1
2. Using the Same Element Twice
Accidentally allowing the same array element to be used twice in the sum.
Why it fails: If target = 4 and numbers = [1, 2, 3], when i = 1 (value 2), the complement is also 2. Without restricting the search range, binary search might return index 1 again.
Solution: Always initialize left = i + 1 to search only in the remaining array.
3. Forgetting to Convert to 1-Based Indexing
The problem explicitly requires 1-indexed results, but the array uses 0-based indexing.
Solution: Always add 1 to both indices:
return [i + 1, first_true_index + 1]
4. Not Checking if first_true_index Contains the Exact Complement
The binary search finds the first position where numbers[mid] >= complement, but this doesn't guarantee equality.
Incorrect approach:
# WRONG: Assumes first_true_index always has the complement return [i + 1, first_true_index + 1]
Solution: Always verify both that first_true_index != -1 and numbers[first_true_index] == complement:
if first_true_index != -1 and numbers[first_true_index] == complement: return [i + 1, first_true_index + 1]
Which of the following is a good use case for backtracking?
Recommended Readings
Tech Interview Pattern Two Pointers Introduction If you prefer videos here's a super quick introduction to Two Pointers div class responsive iframe iframe src https www youtube com embed xZ4AfXHQ1VQ title YouTube video player frameborder 0 allow accelerometer autoplay clipboard write encrypted media gyroscope picture in picture allowfullscreen iframe div Two pointers is a common interview
https assets algo monster cover_photos Binary_Search svg Binary Search Intuition Binary search is an efficient array search algorithm It works by narrowing down the search range by half each time If you have looked up a word in a physical dictionary you've already used binary search in real life Let's
Coding Interview Patterns Your Personal Dijkstra's Algorithm to Landing Your Dream Job The goal of AlgoMonster is to help you get a job in the shortest amount of time possible in a data driven way We compiled datasets of tech interview problems and broke them down by patterns This way
Want a Structured Path to Master System Design Too? Don’t Miss This!