Card 03/ 08
The One-Pass Lookup Template in Python, Java and TypeScript
One loop that looks up what the current item needs and then records the item, with the two lines that change between problems marked.
The one-pass lookup template is a single loop that works out the key the current item needs, looks that key up among the earlier items, and only then records the item. Between problems two lines change, and they are the highlighted ones: the key line, which here computes the complement target − value, and the record line, which here stores the item's index. A counting problem records a count instead, and a grouping problem computes a different key. Each listing runs on 4 1 6 3 8 with the target 9, and on three edge cases: two equal values that do form a pair, no pair at all, and an empty list.
The one-pass pair search on four inputs, in each language
def pair_with_sum(values: list[int], target: int) -> tuple[int, int] | None:
seen: dict[int, int] = {}
for i, value in enumerate(values):
needed = target - value
if needed in seen:
return seen[needed], i
seen[value] = i
return None
for values, target in [([4, 1, 6, 3, 8], 9), ([5, 5], 10), ([1, 2], 10), ([], 4)]:
pair = pair_with_sum(values, target)
answer = "none" if pair is None else f"indices {pair[0]} {pair[1]}"
print(f"{values} target {target}: {answer}")[4, 1, 6, 3, 8] target 9: indices 2 3
[5, 5] target 10: indices 0 1
[1, 2] target 10: none
[] target 4: noneimport java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
public class PairWithSum {
static int[] pairWithSum(int[] values, int target) {
Map<Integer, Integer> seen = new HashMap<>();
for (int i = 0; i < values.length; i++) {
int needed = target - values[i];
if (seen.containsKey(needed)) {
return new int[] {seen.get(needed), i};
}
seen.put(values[i], i);
}
return null;
}
public static void main(String[] args) {
int[][] inputs = {{4, 1, 6, 3, 8}, {5, 5}, {1, 2}, {}};
int[] targets = {9, 10, 10, 4};
for (int k = 0; k < inputs.length; k++) {
int[] pair = pairWithSum(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);
}
}
}[4, 1, 6, 3, 8] target 9: indices 2 3
[5, 5] target 10: indices 0 1
[1, 2] target 10: none
[] target 4: nonefunction pairWithSum(values: number[], target: number): [number, number] | null {
const seen = new Map<number, number>();
for (let i = 0; i < values.length; i++) {
const needed = target - values[i];
const earlier = seen.get(needed);
if (earlier !== undefined) {
return [earlier, i];
}
seen.set(values[i], i);
}
return null;
}
const inputs: [number[], number][] = [
[[4, 1, 6, 3, 8], 9],
[[5, 5], 10],
[[1, 2], 10],
[[], 4],
];
for (const [values, target] of inputs) {
const pair = pairWithSum(values, target);
const answer = pair === null ? "none" : `indices ${pair[0]} ${pair[1]}`;
console.log(`[${values.join(", ")}] target ${target}: ${answer}`);
}[4, 1, 6, 3, 8] target 9: indices 2 3
[5, 5] target 10: indices 0 1
[1, 2] target 10: none
[] target 4: noneThe three outputs match, and the first line names the pair the trace found, indices 2 and 3. The pair 5 5 shows why the lookup comes first: the second 5 finds the first, and neither finds itself. Python's dict, Java's HashMap and TypeScript's Map all give an O(1) lookup on average. The Java version reads the map twice, with containsKey and then get; the TypeScript version calls get once and tests for undefined, which is safe because every value it stores is an index. Returning null or None for no pair makes the caller decide what an absent pair means, the same decision the clarify step asks for.
← → move through sections and cards