Card 03/ 08
The Opposite-Ends Template in Python, Java and TypeScript
Two indices, one comparison, and a move chosen by whether the sum is too small or too large.
The opposite-ends template finds a pair in a sorted array with two indices that start at the ends and move towards each other. The loop runs while left < right, so a value is never paired with itself, and it stops when the pointers meet with no pair found. Two parts change between problems, and the highlighted lines mark them: the test on the current pair, and which pointer each outcome moves. Each listing runs on 2 5 7 8 12 15 for the target 19, on a pair of equal values, on an array with no pair and on an empty array.
The opposite-ends template in three languages
def pair_in_sorted(values: list[int], target: int) -> tuple[int, int] | None:
left, right = 0, len(values) - 1
while left < right:
total = values[left] + values[right]
if total == target:
return left, right
if total < target:
left += 1
else:
right -= 1
return None
for values, target in [([2, 5, 7, 8, 12, 15], 19), ([3, 3], 6), ([1, 2, 4], 10), ([], 4)]:
pair = pair_in_sorted(values, target)
answer = "none" if pair is None else f"indices {pair[0]} {pair[1]}"
print(f"{values} target {target}: {answer}")[2, 5, 7, 8, 12, 15] target 19: indices 2 4
[3, 3] target 6: indices 0 1
[1, 2, 4] target 10: none
[] target 4: noneimport java.util.Arrays;
public class PairInSorted {
static int[] pairInSorted(int[] values, int target) {
int left = 0;
int right = values.length - 1;
while (left < right) {
int total = values[left] + values[right];
if (total == target) {
return new int[] {left, right};
}
if (total < target) {
left++;
} else {
right--;
}
}
return null;
}
public static void main(String[] args) {
int[][] inputs = {{2, 5, 7, 8, 12, 15}, {3, 3}, {1, 2, 4}, {}};
int[] targets = {19, 6, 10, 4};
for (int k = 0; k < inputs.length; k++) {
int[] pair = pairInSorted(inputs[k], targets[k]);
String answer = pair == null ? "none" : "indices " + pair[0] + " " + pair[1];
System.out.println(Arrays.toString(inputs[k]) + " target " + targets[k] + ": " + answer);
}
}
}[2, 5, 7, 8, 12, 15] target 19: indices 2 4
[3, 3] target 6: indices 0 1
[1, 2, 4] target 10: none
[] target 4: nonefunction pairInSorted(values: number[], target: number): [number, number] | null {
let left = 0;
let right = values.length - 1;
while (left < right) {
const total = values[left] + values[right];
if (total === target) {
return [left, right];
}
if (total < target) {
left++;
} else {
right--;
}
}
return null;
}
const inputs: [number[], number][] = [
[[2, 5, 7, 8, 12, 15], 19],
[[3, 3], 6],
[[1, 2, 4], 10],
[[], 4],
];
for (const [values, target] of inputs) {
const pair = pairInSorted(values, target);
const answer = pair === null ? "none" : `indices ${pair[0]} ${pair[1]}`;
console.log(`[${values.join(", ")}] target ${target}: ${answer}`);
}[2, 5, 7, 8, 12, 15] target 19: indices 2 4
[3, 3] target 6: indices 0 1
[1, 2, 4] target 10: none
[] target 4: noneThe three listings print the same answers. An empty array sets right to −1, so the loop never starts and the answer is none without a special case. The pass is O(n) time and O(1) extra space for n values, and it assumes the array is already sorted: on unsorted input it can walk past a pair that exists. A problem that asks for values rather than indices may sort first, at O(n log n), and TypeScript's sort then needs the comparator (a, b) => a - b, because its default compares numbers as strings.
← → move through sections and cards