Use the two-pointer technique, starting at opposite ends of the ordered sequence:
l = 0
r = n - 1
while l < r:
s = S[l] + S[r]
if s == x: return (l, r)
if s < x: l = l + 1
else: r = r - 1
return NONEIf the sum is too small, every remaining pair using
l has sum at most , so discard l. If the sum is too large, every remaining pair using r has sum at least , so discard r. The loop invariant says that every solution not already excluded has both indices in . These two elimination arguments preserve it; at termination fewer than two positions remain.Each unsuccessful iteration decreases by one. Therefore the two-sum problem is solved withThe condition
l < r ensures distinct positions and handles . Under the usual interpretation of “two distinct elements” as two different occurrences, equal values in different positions are allowed. If distinct numerical values are intended instead, reject an equality with : sortedness then makes the entire remaining interval equal, so it contains no distinct-value solution. Both interpretations retain linear time complexity. The comparison assumes exact real-number operations, as in the stated comparison model.Search only the suffix strictly after each candidate first index. This enforces distinct indices even when the target is twice an existing value. Compute the complement in
Long to prevent integer overflow: subtracting two Int values can require 33 signed bits.def twoSum(a: Array[Int], target: Int): (Int, Int) = {
def lowerBound(from: Int, wanted: Long): Int = {
var lo = from
var hi = a.length
while (lo < hi) {
val mid = lo + (hi - lo) / 2
if (a(mid).toLong < wanted) lo = mid + 1
else hi = mid
}
lo
}
var i = 0
while (i < a.length - 1) {
val wanted = target.toLong - a(i).toLong
val j = lowerBound(i + 1, wanted)
if (j < a.length && a(j).toLong == wanted) return (i, j)
i += 1
}
(0, 0)
}The outer loop invariant says that no solution has a smaller first index than
i, when a pair is written in increasing index order. A failed suffix binary search proves there is no partner for the current i, extending the excluded prefix by one. A successful return has and in exact integer arithmetic. At exhaustion, every possible ordered pair has been excluded, so the sentinel (0,0) means no solution. The inner loop invariant is the standard lower-bound partition, restricted to the suffix starting at i + 1.There are at most suffix searches, each taking running time, so the total is , customarily written for . Auxiliary space is . Empty and singleton arrays immediately return the sentinel. This is distinct-index two-sum by suffix lower bounds; a faster two-pointer technique is also possible on a sorted array, but the code here directly meets the requested search-based bound.
Two-pointer technique Created 2026-10-20 Updated 2026-11-05
The two-pointer technique maintains two indices whose monotone movement avoids revisiting candidate pairs. To find two values summing to in an increasing array, start at its ends. If their sum is too small, move the left index right: every pair using the old left value is too small. If too large, move the right index left by the symmetric argument. Each step eliminates one index, so after sorting the scan takes time. The correctness depends on sorted order; blindly applying the rule to unsorted inputs can discard a valid pair.
Two-sum problem 2026-10-24
Find two distinct input positions whose values sum to a target, or establish that no such pair exists. Distinct values are not required: two equal values may form a pair when they occur at different positions. On a sorted array, distinct-index two-sum by suffix lower bounds gives running time, while a two-pointer technique can give linear time.