Facebook Pixel

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:

  1. The array uses 1-based indexing (the first element is at index 1, not 0)
  2. You need to find exactly two different elements whose sum equals the target
  3. Return the indices of these two numbers as [index1, index2] where index1 < index2
  4. Since the array is 1-indexed, you need to add 1 to the actual array positions
  5. You cannot use the same element twice (the two numbers must be at different positions)
  6. The problem guarantees exactly one solution exists
  7. 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.

Quick Interview Experience
Help others by sharing your interview experience
Have you seen this problem before?

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:

  1. Pick a number from the array (let's call it numbers[i])
  2. Calculate what value we need to find: complement = target - numbers[i]
  3. Since the array is sorted, we can use binary search to quickly check if complement exists 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
41
1class 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}
43
1class 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};
37
1/**
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}
39

Solution 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:

  1. Iterate through the array: We loop through indices from 0 to n-2 (where n is 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.

  2. Calculate the complement: For each numbers[i], we calculate the required complement:

    complement = target - numbers[i]
  3. Binary search with template: We use the boundary-finding template:

    • Initialize left = i + 1, right = n - 1, and first_true_index = -1
    • The feasible condition is numbers[mid] >= complement
    • When feasible, save first_true_index = mid and search left with right = mid - 1
    • When not feasible, search right with left = mid + 1
  4. 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 position
    • numbers[first_true_index] == complement: Confirms the value equals complement (not just >= complement)
  5. 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 Evaluator

Example 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] >= 9 in range [1, 4]:
    • left = 1, right = 4, first_true_index = -1
    • mid = 2: numbers[2] = 4 < 9left = 3
    • mid = 3: numbers[3] = 7 < 9left = 4
    • mid = 4: numbers[4] = 11 >= 9first_true_index = 4, right = 3
    • left > right, exit loop
  • Check: first_true_index = 4, but numbers[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] >= 7 in range [2, 4]:
    • left = 2, right = 4, first_true_index = -1
    • mid = 3: numbers[3] = 7 >= 7first_true_index = 3, right = 2
    • mid = 2: numbers[2] = 4 < 7left = 3
    • left > right, exit loop
  • Check: first_true_index = 3, and numbers[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 - 1 times, iterating through each element as a potential first number
  • For each iteration, bisect_left performs a binary search on the remaining portion of the array (from index i + 1 to the end), which takes O(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]
Loading...
Discover Your Strengths and Weaknesses: Take Our 5-Minute Quiz to Tailor Your Study Plan:

Which of the following is a good use case for backtracking?


Recommended Readings

Want a Structured Path to Master System Design Too? Don’t Miss This!

Load More