Run your code 2 more times to open the solutions. The idea sticks better after an attempt. Two Sum in Python
2 reference approaches in Python, from brute force to the one to reach for. Compare them with yours.
Try each index i with every later index j. Starting j at i + 1 skips pairing a number with itself and never checks the same pair twice.
def two_sum(nums: list[int], target: int) -> list[int]:
for i in range(len(nums)):
for j in range(i + 1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
return []
For each number n, the partner it needs is target - n. Keep a dictionary of the values you have already passed and where you saw them. If the partner is in it, you are done; if not, store n and move on. A dictionary lookup takes constant time on average, so the whole walk is linear.
def two_sum(nums: list[int], target: int) -> list[int]:
seen: dict[int, int] = {} # value -> index where we saw it
for i, n in enumerate(nums):
need = target - n
if need in seen:
return [seen[need], i]
seen[n] = i
return []
Two Sum in JavaScript
2 reference approaches in JavaScript, from brute force to the one to reach for. Compare them with yours.
Two classic for loops with let counters. The inner counter j starts at i + 1, so an element is never added to itself and every pair is tested once. The comparison uses ===, and the function returns from inside both loops the moment a sum matches.
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) return [i, j];
}
}
return [];
}
nums.entries() yields [index, value] pairs, which for…of destructures into i and n. The partner that completes n is target - n; seen.get returns its index if an earlier element had that value, or undefined if not. On a miss the loop records n and moves on. Map lookups and inserts are constant time on average, so the whole function is linear.
function twoSum(nums, target) {
const seen = new Map(); // value -> index of its latest sighting
for (const [i, n] of nums.entries()) {
const j = seen.get(target - n);
if (j !== undefined) return [j, i];
seen.set(n, i);
}
return [];
}
Two Sum in TypeScript
2 reference approaches in TypeScript, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. Starting j at i + 1 never pairs a number with itself and never checks the same pair twice.
function twoSum(nums: number[], target: number): number[] {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) return [i, j];
}
}
return [];
}
One pass with a Map<number, number> from value to index. For each number, look up the partner target - n; get answers in constant time, and the current number is stored only after the lookup, so a value never pairs with itself.
function twoSum(nums: number[], target: number): number[] {
const seen = new Map<number, number>();
for (let i = 0; i < nums.length; i++) {
const j = seen.get(target - nums[i]);
if (j !== undefined) return [j, i];
seen.set(nums[i], i);
}
return [];
}
Two Sum in Java
2 reference approaches in Java, from brute force to the one to reach for. Compare them with yours.
The outer counter i picks the first element and the inner counter j scans everything to its right, starting at i + 1. Because j is always ahead of i, no element is summed with itself and no pair is tested in both orders. A match comes back as new int[] {i, j}. In main, each result goes through Arrays.toString: passing an int[] straight to println would show the array's type tag and a hash code, not its elements.
import java.util.*;
public class Main {
static int[] twoSum(int[] nums, int target) {
for (int i = 0; i < nums.length; i++) {
for (int j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) {
return new int[] {i, j};
}
}
}
return new int[0];
}
}
At position i, the only value that can complete a pair is target - nums[i]. The map seen remembers every value to the left of i along with where it was found, so one get answers whether that complement has already appeared. If it has, the method returns both positions; otherwise put files the current element and the loop advances. Hash lookups average constant time, so the method is linear. The map is created with HashMap.newHashMap(nums.length), a Java 19 factory that picks a capacity large enough for that many entries, so no resize happens mid-loop.
import java.util.*;
public class Main {
static int[] twoSum(int[] nums, int target) {
HashMap<Integer, Integer> seen = HashMap.newHashMap(nums.length); // value -> index where we saw it
for (int i = 0; i < nums.length; i++) {
Integer j = seen.get(target - nums[i]);
if (j != null) {
return new int[] {j, i};
}
seen.put(nums[i], i);
}
return new int[0];
}
}
Two Sum in Go
2 reference approaches in Go, from brute force to the one to reach for. Compare them with yours.
The outer loop is for i := range nums; with a single variable, range yields just the index. The inner loop is Go's three-clause for, and its counter j begins one past i. That start point means an element is never added to itself and each unordered pair comes up exactly once. The first hit returns a fresh two-element slice literal.
package main
func twoSum(nums []int, target int) []int {
for i := range nums {
for j := i + 1; j < len(nums); j++ {
if nums[i]+nums[j] == target {
return []int{i, j}
}
}
}
return []int{}
}
Here range hands over both the index i and the value n. Whatever pairs with n has to equal target-n, so the loop asks the seen map whether an earlier element held that value. A hit returns the stored index together with i; a miss records seen[n] = i and the loop continues. Each map read or write is constant time on average, which makes the function linear overall. The capacity hint in make(map[int]int, len(nums)) lets the runtime allocate room up front instead of rehashing as entries arrive.
package main
func twoSum(nums []int, target int) []int {
seen := make(map[int]int, len(nums)) // value -> index where we saw it
for i, n := range nums {
if j, ok := seen[target-n]; ok {
return []int{j, i}
}
seen[n] = i
}
return []int{}
}
Two Sum in C#
2 reference approaches in C#, from brute force to the one to reach for. Compare them with yours.
Try each index i with every later index j. Starting j at i + 1 skips pairing a number with itself and never checks the same pair twice.
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution
{
public static int[] TwoSum(int[] nums, int target)
{
for (int i = 0; i < nums.Length; i++)
{
for (int j = i + 1; j < nums.Length; j++)
{
if (nums[i] + nums[j] == target) return new[] { i, j };
}
}
return new int[0];
}
}
One pass with a Dictionary<int, int> from value to index. For each number, ask whether its partner target - n has been seen; TryGetValue answers in constant time, and the current number is stored only after the check, so a value never pairs with itself.
using System;
using System.Collections.Generic;
using System.Linq;
public class Solution
{
public static int[] TwoSum(int[] nums, int target)
{
var seen = new Dictionary<int, int>();
for (int i = 0; i < nums.Length; i++)
{
if (seen.TryGetValue(target - nums[i], out int j)) return new[] { j, i };
seen[nums[i]] = i;
}
return new int[0];
}
}
Two Sum in C++
2 reference approaches in C++, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. Starting j at i + 1 never pairs a number with itself and never checks the same pair twice. Taking nums by reference avoids copying the vector on every call.
#include <algorithm>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for (int i = 0; i < (int)nums.size(); i++) {
for (int j = i + 1; j < (int)nums.size(); j++) {
if (nums[i] + nums[j] == target) return {i, j};
}
}
return {};
}
};
One pass with an unordered_map<int, int> from value to index. find looks the partner target - n up in constant average time, and the current number is inserted only after the lookup, so a value never pairs with itself.
#include <algorithm>
#include <string>
#include <unordered_map>
#include <unordered_set>
#include <vector>
using namespace std;
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int, int> seen;
for (int i = 0; i < (int)nums.size(); i++) {
auto it = seen.find(target - nums[i]);
if (it != seen.end()) return {it->second, i};
seen[nums[i]] = i;
}
return {};
}
};
Two Sum in C
2 reference approaches in C, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. When a pair matches, allocate two ints with malloc, fill them, set *returnSize = 2 and return the pointer. No pair means a size of 0 and NULL.
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
/**
* Note: the returned array must be malloc'ed; the caller frees it.
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
for (int i = 0; i < numsSize; i++) {
for (int j = i + 1; j < numsSize; j++) {
if (nums[i] + nums[j] == target) {
int* result = malloc(2 * sizeof *result);
result[0] = i;
result[1] = j;
*returnSize = 2;
return result;
}
}
}
*returnSize = 0;
return NULL;
}
C has no built-in hash map, so build a small one: an open-addressing table twice the input's size, keyed by value and storing the index. Probe linearly from the key's hash until an empty slot or the partner target - n turns up; insert the current number only after the lookup.
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
/**
* Note: the returned array must be malloc'ed; the caller frees it.
*/
int* twoSum(int* nums, int numsSize, int target, int* returnSize) {
int capacity = 1;
while (capacity < 2 * numsSize) capacity <<= 1;
int* keys = malloc(capacity * sizeof *keys);
int* index = malloc(capacity * sizeof *index);
bool* used = calloc(capacity, sizeof *used);
int* result = NULL;
*returnSize = 0;
for (int i = 0; i < numsSize && !result; i++) {
int want = target - nums[i];
unsigned slot = (unsigned)want * 2654435761u & (unsigned)(capacity - 1);
while (used[slot]) {
if (keys[slot] == want) {
result = malloc(2 * sizeof *result);
result[0] = index[slot];
result[1] = i;
*returnSize = 2;
break;
}
slot = (slot + 1) & (unsigned)(capacity - 1);
}
if (result) break;
slot = (unsigned)nums[i] * 2654435761u & (unsigned)(capacity - 1);
while (used[slot] && keys[slot] != nums[i]) slot = (slot + 1) & (unsigned)(capacity - 1);
if (!used[slot]) {
used[slot] = true;
keys[slot] = nums[i];
index[slot] = i;
}
}
free(keys);
free(index);
free(used);
return result;
}
Two Sum in Kotlin
2 reference approaches in Kotlin, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. Starting j at i + 1 never pairs a number with itself and never checks the same pair twice.
class Solution {
fun twoSum(nums: IntArray, target: Int): IntArray {
for (i in nums.indices) {
for (j in i + 1 until nums.size) {
if (nums[i] + nums[j] == target) return intArrayOf(i, j)
}
}
return intArrayOf()
}
}
One pass with a HashMap<Int, Int> from value to index. Look the partner target - n up first, then store the current number, so a value never pairs with itself and the smaller index comes first.
class Solution {
fun twoSum(nums: IntArray, target: Int): IntArray {
val seen = HashMap<Int, Int>()
for ((i, n) in nums.withIndex()) {
val j = seen[target - n]
if (j != null) return intArrayOf(j, i)
seen[n] = i
}
return intArrayOf()
}
}
Two Sum in Rust
2 reference approaches in Rust, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. Indices are usize in Rust, so the pair is cast back to i32 for the result.
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
for i in 0..nums.len() {
for j in i + 1..nums.len() {
if nums[i] + nums[j] == target {
return vec![i as i32, j as i32];
}
}
}
vec![]
}
}
One pass with a HashMap<i32, i32> from value to index. get returns an Option, so if let Some(&j) handles the lookup; the current number is inserted only after it, so a value never pairs with itself.
use std::collections::HashMap;
impl Solution {
pub fn two_sum(nums: Vec<i32>, target: i32) -> Vec<i32> {
let mut seen: HashMap<i32, i32> = HashMap::with_capacity(nums.len());
for (i, &n) in nums.iter().enumerate() {
if let Some(&j) = seen.get(&(target - n)) {
return vec![j, i as i32];
}
seen.insert(n, i as i32);
}
vec![]
}
}
Two Sum in Swift
2 reference approaches in Swift, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. The inner range starts at i + 1, so a number never pairs with itself and no pair is checked twice.
class Solution {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
for i in 0..<nums.count {
for j in (i + 1)..<nums.count where nums[i] + nums[j] == target {
return [i, j]
}
}
return []
}
}
One pass with a [Int: Int] dictionary from value to index. A subscript lookup returns an optional, so if let unwraps a found partner; the current number is stored only after the lookup, so a value never pairs with itself.
class Solution {
func twoSum(_ nums: [Int], _ target: Int) -> [Int] {
var seen: [Int: Int] = [:]
for (i, n) in nums.enumerated() {
if let j = seen[target - n] {
return [j, i]
}
seen[n] = i
}
return []
}
}
Two Sum in PHP
2 reference approaches in PHP, from brute force to the one to reach for. Compare them with yours.
Pair each index $i with every later index $j. Starting $j at $i + 1 never pairs a number with itself and never checks the same pair twice.
<?php
class Solution {
/**
* @param int[] $nums
* @param int $target
* @return int[]
*/
function twoSum(array $nums, int $target): array {
$n = count($nums);
for ($i = 0; $i < $n; $i++) {
for ($j = $i + 1; $j < $n; $j++) {
if ($nums[$i] + $nums[$j] === $target) {
return [$i, $j];
}
}
}
return [];
}
}
One pass with an associative array from value to index. isset checks for the partner $target - $n in constant time, and the current number is stored only after the check, so a value never pairs with itself.
<?php
class Solution {
/**
* @param int[] $nums
* @param int $target
* @return int[]
*/
function twoSum(array $nums, int $target): array {
$seen = [];
foreach ($nums as $i => $n) {
$need = $target - $n;
if (isset($seen[$need])) {
return [$seen[$need], $i];
}
$seen[$n] = $i;
}
return [];
}
}
Two Sum in Ruby
2 reference approaches in Ruby, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. The inner range starts at i + 1, so a number never pairs with itself and no pair is checked twice.
# @param {Integer[]} nums
# @param {Integer} target
# @return {Integer[]}
def two_sum(nums, target)
(0...nums.length).each do |i|
((i + 1)...nums.length).each do |j|
return [i, j] if nums[i] + nums[j] == target
end
end
[]
end
One pass with a Hash from value to index. Look the partner target - n up first, then store the current number, so a value never pairs with itself and the smaller index comes first.
# @param {Integer[]} nums
# @param {Integer} target
# @return {Integer[]}
def two_sum(nums, target)
seen = {}
nums.each_with_index do |n, i|
j = seen[target - n]
return [j, i] if j
seen[n] = i
end
[]
end
Two Sum in Dart
2 reference approaches in Dart, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. Starting j at i + 1 never pairs a number with itself and never checks the same pair twice.
class Solution {
List<int> twoSum(List<int> nums, int target) {
for (var i = 0; i < nums.length; i++) {
for (var j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] == target) return [i, j];
}
}
return [];
}
}
One pass with a Map<int, int> from value to index. The lookup returns int?, so a null check tells a found partner from a missing one; the current number is stored only after the lookup.
class Solution {
List<int> twoSum(List<int> nums, int target) {
final seen = <int, int>{};
for (var i = 0; i < nums.length; i++) {
final j = seen[target - nums[i]];
if (j != null) return [j, i];
seen[nums[i]] = i;
}
return [];
}
}
Two Sum in Scala
2 reference approaches in Scala, from brute force to the one to reach for. Compare them with yours.
Pair each index i with every later index j. The method body is a boundary, so break(Array(i, j)) leaves both loops with the pair as soon as one matches.
import scala.util.boundary, boundary.break
object Solution {
def twoSum(nums: Array[Int], target: Int): Array[Int] = boundary {
for (i <- nums.indices; j <- i + 1 until nums.length) {
if (nums(i) + nums(j) == target) break(Array(i, j))
}
Array.empty[Int]
}
}
One pass with a mutable.HashMap[Int, Int] from value to index. get returns an Option; a found partner breaks out of the boundary with the pair, and the current number is stored only after the lookup, so a value never pairs with itself.
import scala.collection.mutable
import scala.util.boundary, boundary.break
object Solution {
def twoSum(nums: Array[Int], target: Int): Array[Int] = boundary {
val seen = mutable.HashMap.empty[Int, Int]
for (i <- nums.indices) {
seen.get(target - nums(i)) match {
case Some(j) => break(Array(j, i))
case None => seen(nums(i)) = i
}
}
Array.empty[Int]
}
}
FAQ for Python
What if the list contains duplicates?
Both solutions handle them. Two equal values at different indices are a valid pair, as in [5, 1, 5] with target 10. The dictionary keeps the latest index for each value, but the check runs before the store, so the earlier copy is found first.
How do I return the two values instead of their indices?
Return [need, n] instead of [seen[need], i]. If you only need the values, a set is enough: seen = set(), then if target - n in seen: return [target - n, n] and seen.add(n).
Can Two Sum be solved in O(1) extra space?
Only by giving up linear time. If the list is already sorted, two pointers moving in from both ends find the pair in O(n) time and O(1) space. On an unsorted list you would have to sort it first, which costs O(n log n) and loses the original indices unless you sort (value, index) pairs, which takes O(n) space again.
What if more than one pair adds up to the target?
The hash map returns the pair that completes first while scanning left to right. To get every pair, keep a list of indices per value in the dictionary and append a result each time a partner is found instead of returning.
Why not use list.index() to find the partner?
Because list.index scans the list, so calling it inside a loop is O(n²) again, and it returns the first match, which can be the number itself. The dictionary lookup is what makes the solution linear.
FAQ for JavaScript
Can I use a plain object instead of a Map?
It works if you are careful: number keys are turned into strings, which still match on lookup. The usual bug is the check. Writing if (seen[target - nums[i]]) treats index 0 as missing, so [5, 1, 5] with target 10 returns [] instead of [ 0, 2 ]. Compare against undefined or use in, or just use a Map, which keeps number keys as numbers and has no inherited properties.
Why does it find nothing when the numbers come from a text input?
Because they are strings. '3' + '9' is '39', not 12, and a Map keyed by '3' never matches the number 3. Both versions return [] for ['3', '9', '14', '21'] with target 23. Convert first: '3 9 14 21'.split(' ').map(Number) gives an array that returns [ 1, 2 ].
Is there an O(1) extra space solution?
Only on a sorted array: one pointer at each end, moving inward depending on whether the sum is too small or too big, takes O(n) time and no extra memory. Sorting an unsorted array costs O(n log n) and loses the original indices. Pass a comparator, too: [3, 9, 14, 21].toSorted() gives [ 14, 21, 3, 9 ] because the default sort compares strings, while toSorted((a, b) => a - b) gives [ 3, 9, 14, 21 ].
What if several pairs reach the target?
The two versions can disagree. The nested loop returns the pair with the smallest first index, the Map returns the pair whose second element comes earliest. For [1, 2, 3, 4] and target 5, the brute force prints [ 0, 3 ] and the Map prints [ 1, 2 ]. Both are valid; the statement allows only one answer so the choice never matters there.
Do very large numbers break the sum?
They lose precision instead of wrapping. Past Number.MAX_SAFE_INTEGER (2^53 - 1), 2 ** 53 + 1 === 2 ** 53 is true, so the brute force reports [ 0, 1 ] for [2 ** 53, 1] with target 2 ** 53. Pass BigInt values instead ([2n ** 53n, 1n] and a 2n ** 53n target): both functions run unchanged and correctly return [].
FAQ for TypeScript
Why check j !== undefined instead of if (j)?
seen.get returns undefined for a missing key, but a stored index can be 0, which is falsy. if (j) would skip a partner at index 0; comparing with undefined keeps it.
Map or a plain object for the lookup?
A Map<number, number> keeps number keys as numbers and has no prototype keys to trip over. A plain object turns every key into a string and needs Object.create(null) or hasOwnProperty checks to be safe.
Should the return type be a tuple?
[number, number] | [] describes the result more exactly than number[], and callers then get a compile error if they read a third index. The tests compare values, so either annotation passes.
Why not nums.indexOf(target - n)?
indexOf scans the array on every call, which brings the loop back to O(n²), and it can return the current index when the partner equals the value itself.
Does TypeScript change how the code runs?
No. The types are checked and then removed, so the compiled JavaScript runs exactly like the untyped version. Types catch mistakes such as returning nums[i] instead of i before the code runs.
FAQ for Java
Should twoSum return null or an empty array when there is no pair?
An empty new int[0] is kinder to callers. It has length 0, an enhanced for loop over it does nothing, and Arrays.toString renders it as []. A null return pushes a check onto every call site: forget it and result.length throws a NullPointerException, while Arrays.toString quietly prints the word null.
Why is the lookup result stored in an Integer instead of an int?
seen.get(key) returns null when the key is absent. Assigning that straight to an int makes Java auto-unbox it, and unboxing null throws a NullPointerException. Keeping it as Integer j lets the code test j != null, which costs one hash lookup; the alternative of containsKey followed by get hashes the same key twice.
How much does boxing cost in the HashMap version?
A constant factor, not a change in complexity. Generic maps hold objects, so every put turns the int value and the int index into Integer objects. Integer.valueOf hands out cached instances only for -128 to 127 by default; anything outside that range is a new allocation. The one-pass map still beats the O(n²) loops by a wide margin on large inputs. Boxing has a second trap: == on two Integer objects compares references, so values outside the cache can compare unequal even when they hold the same number. Use equals for boxed values. The JDK itself has no primitive-keyed map.
Can int overflow give a wrong answer?
It can on extreme inputs. Java int arithmetic wraps silently at 32 bits, so two large positive elements may add up to a negative number, and if that wrapped sum equals target the double loop reports a pair whose real sum is different. The map version has the same blind spot, since target - nums[i] wraps as well. Doing the math in long closes the gap: (long) nums[i] + nums[j] == target in the loop, or long need = (long) target - nums[i] and skipping any need outside the int range in the map version. Math.addExact is the alternative when you would rather get an ArithmeticException than a wrapped value.
What if the input array is already sorted?
Then two indices can close in from both ends with no map at all: int lo = 0, hi = nums.length - 1; and while (lo < hi). If nums[lo] + nums[hi] hits the target, return new int[] {lo, hi}; if it falls short, lo++; if it overshoots, hi--. With {1, 3, 4, 6, 9} and a target of 10 the outer pair 1 and 9 matches at once, giving positions 0 and 4. That is O(n) time and O(1) extra space with no boxing. Calling Arrays.sort on an unsorted int[] would lose the original positions, so you would sort an Integer[] of indices with a comparator on nums instead, for O(n log n) time and O(n) space.
FAQ for Go
Is it better to return nil or []int{} when nothing matches?
Most callers cannot tell them apart: len(res) == 0 holds for both, a for range over either runs zero times, and printing either shows []. They differ in two places. res == nil is true only for the nil slice, and encoding/json marshals a nil slice as null but []int{} as []. The code here returns []int{} so a JSON response would always carry an array.
Why does the map lookup need the comma-ok form?
Reading a key that is not in a Go map does not panic or return an error; it gives the zero value of the value type. For a map[int]int that zero is 0, which looks exactly like a partner stored at index 0. j, ok := seen[target-n] adds a boolean that is false only when the key was never written, so the if fires on genuine matches alone.
Would returning (int, int, bool) be more idiomatic than a slice?
It is a common Go shape. A signature such as func twoSum(nums []int, target int) (int, int, bool) follows the same comma-ok convention as map reads and type assertions, and the caller writes i, j, ok := twoSum(nums, target) then checks ok. It also removes the nil-or-empty question entirely. The slice version is kept here so the printed results line up with the examples.
What changes if the slice is already sorted?
You can drop the map and walk two indices toward each other: lo, hi := 0, len(nums)-1, then for lo < hi. Return []int{lo, hi} when the sum equals the target, move lo++ when it is too small and hi-- when it is too big. On []int{1, 3, 4, 6, 9} with target 10, the ends 1 and 9 match immediately, giving positions 0 and 4. That runs in O(n) time with O(1) extra space. An unsorted input cannot simply go through slices.Sort, because sorting in place loses the original positions; sorting a separate slice of indices with slices.SortFunc keeps them, at O(n log n) time and O(n) space.
How do I get the two values rather than their positions?
Return []int{target - n, n} from the hit branch. Once positions no longer matter, the map only needs to answer "have I seen this value", and Go has no set type, so the usual choice is map[int]struct{}: seen[n] = struct{}{} records a value without storing anything extra, and _, ok := seen[target-n] checks for one.
FAQ for C#
Why not use Array.IndexOf to find the partner?
Array.IndexOf(nums, target - nums[i]) scans the array every time, so the loop is O(n²) again, and it can return i itself when the partner equals the current value. The dictionary remembers positions as you go and only ever holds earlier indices.
Why TryGetValue instead of ContainsKey and then the indexer?
ContainsKey followed by seen[key] looks the key up twice. TryGetValue(key, out int j) does one lookup and hands back the index in the same call.
What if nums has duplicate values?
The dictionary stores the latest index for a value, but the check happens before the store, so [5, 1, 5] with target 10 finds the first 5 (index 0) when it reaches the second one and returns [0, 2].
Why return new int[0] instead of null?
Callers can loop over an empty array without a null check, and the tests expect an empty array for "no pair". Array.Empty<int>() is the allocation-free equivalent.
Can the sum overflow an int?
Two values near int.MaxValue would overflow silently in a checked-off build. The constraints keep values within ±10⁹, so the sum fits; for wider ranges compute target - nums[i] as a long.
FAQ for C++
Why find instead of the [] operator for the lookup?
seen[key] inserts a default 0 when the key is missing, so a lookup would both add a bogus entry and report index 0 as a partner. find returns end() for a missing key and changes nothing.
Why cast nums.size() to int in the loops?
size() returns an unsigned size_t. Comparing it with a signed int triggers a sign-compare warning, and arithmetic like nums.size() - 1 wraps around to a huge number on an empty vector.
Should I use bits/stdc++.h?
It compiles on GCC but pulls in the whole standard library, which costs a couple of seconds on every Run here. Include what you use: <vector>, <unordered_map>, and so on.
Is unordered_map always O(1)?
On average. Many keys in one bucket make a lookup linear; calling seen.reserve(nums.size()) up front avoids rehashing as the map grows.
Can the sum overflow?
Two int values near the limits overflow, which is undefined behaviour in C++. The constraints keep values within ±10⁹, so target - nums[i] fits; for wider ranges compute it in long long.
FAQ for C
What is returnSize for?
A C function returns one value, and a pointer does not carry its length. The caller passes the address of an int; the function writes the result's length there, 2 for a pair or 0 for none, so the caller knows how many ints to read.
Why malloc the result instead of returning a local array?
A local array lives on the stack and is gone once the function returns, so the caller would read freed memory. malloc puts the result on the heap, and the caller frees it when done.
Why only keep the first index of a repeated value?
The lookup happens before the insert, so [5, 1, 5] with target 10 finds the stored 5 at index 0 when it reaches the second 5. Skipping the insert for a key already present keeps the earliest index, which the smaller-index-first rule wants.
Why cast to unsigned before hashing?
Multiplying signed ints can overflow, which is undefined behaviour in C. Unsigned arithmetic wraps by definition, and masking with capacity - 1 (a power of two) keeps the slot in range for negative values too.
Can target - nums[i] overflow?
With values within ±10⁹ the difference fits in an int. For the full int range compute it as a long long and store long long keys.
FAQ for Kotlin
Why IntArray and not List<Int>?
IntArray compiles to a primitive int[] on the JVM, so there is no boxing per element. List<Int> holds boxed Integer objects. The signature uses IntArray because the input is a fixed-size array of numbers.
What does seen[target - n] return when the key is missing?
The map's get operator returns Int?, so a missing key is null. Checking j != null smart-casts j to Int inside the branch, with no !! needed.
Why withIndex() instead of indices?
for ((i, n) in nums.withIndex()) gives the index and the value together, which reads closer to the idea of the algorithm. for (i in nums.indices) with nums[i] compiles to the same loop and is fine too.
Can I use mutableMapOf() instead of HashMap()?
mutableMapOf() returns a LinkedHashMap, which also keeps insertion order. That costs a little memory and nothing here needs the order, so HashMap is the leaner choice, but both pass.
Can the sum overflow?
An Int holds about ±2.1 × 10⁹. The constraints keep values within ±10⁹, so target - n fits. For wider values use Long for the arithmetic.
FAQ for Rust
Why cast the indices with as i32?
Rust indexes slices with usize, but the signature returns Vec<i32>. i as i32 converts each index; the input is far smaller than i32::MAX, so nothing is lost.
Why seen.get(&(target - n)) with a reference?
HashMap::get takes a reference to the key, so the computed partner is borrowed with &. The parentheses make the subtraction happen before the borrow.
What does if let Some(&j) do?
get returns Option<&i32>. The pattern Some(&j) matches a found entry and copies the i32 out of the reference, so j is a plain number inside the branch.
Why HashMap::with_capacity?
It reserves room for every number up front, so the map never rehashes while it grows. HashMap::new() passes too; the capacity only saves a few reallocations.
Can target - n overflow?
In a debug build an i32 overflow panics. The constraints keep values within ±10⁹, so the difference fits. For the full i32 range, compute it with i64 or checked_sub.
FAQ for Swift
Why does (i + 1)..<nums.count not crash on the last index?
For the last i, the range is nums.count..<nums.count, which is empty, so the inner loop simply does not run. A closed range ... with a lower bound above the upper bound would trap, which is why the half-open ..< is used.
What does seen[target - n] return for a missing key?
A dictionary subscript returns Int?, nil when the key is missing. if let j = … runs the branch only for a found index, with j unwrapped to Int.
Why enumerated() instead of indices?
nums.enumerated() yields (offset, element) pairs, so the loop gets the index and the value together. for i in nums.indices with nums[i] is equivalent.
Should I reserve capacity for the dictionary?
seen.reserveCapacity(nums.count) avoids regrowing while the dictionary fills. It is an optimisation only; the solution is O(n) either way.
Why the underscores in func twoSum(_ nums: [Int], _ target: Int)?
The underscore drops the argument label, so the caller writes twoSum(nums, target) instead of twoSum(nums: nums, target: target). The runner calls the method that way.
FAQ for PHP
Why isset instead of array_key_exists?
isset($seen[$need]) is a language construct and the fastest key check. It returns false for a key whose value is null, but every stored value here is an index, never null, so the two behave the same.
Can negative numbers be array keys?
Yes. PHP arrays are ordered hash maps, and any integer, negative or not, is a valid key. Integer-like strings are converted to integers as keys too.
Why not array_search for the partner?
array_search scans the whole array on every call, which makes the loop O(n²) again, and it can return the current index when the partner equals the number itself.
Why count($nums) once before the loops?
count is O(1) on arrays, so calling it in the loop condition is not a real cost. Storing it in $n just keeps the conditions short.
Why === in the comparison?
Strict comparison checks the type as well as the value. With integer input both work, but === avoids PHP's loose conversions, a good habit in every solution.
FAQ for Ruby
Why does return inside each_with_index leave the method?
A block passed with do ... end is not a separate method, so return in it returns from two_sum itself, ending the loop at the first match.
Is if j safe when the stored index is 0?
Yes. In Ruby only nil and false are falsy; 0 is truthy. A missing key gives nil, and a stored index of 0 still counts as found.
Why not nums.index(target - n)?
Array#index scans the array on every call, which makes the loop O(n²) again, and it can return the current position when the partner equals the number itself.
Why (i + 1)...nums.length with three dots?
Three dots build an exclusive range that stops before nums.length, the last valid index plus one. Two dots would include nums.length and read nil past the end.
Could combination(2) solve it?
nums.each_with_index.to_a.combination(2).find { ... } reads nicely but builds every pair, O(n²) time and lots of short-lived arrays. The hash pass is linear.
FAQ for Dart
Why final seen = <int, int>{} and not var seen = {}?
An empty {} without type arguments is a Map<dynamic, dynamic>. The <int, int> type arguments keep the keys and values typed, and final says the variable is never reassigned (the map itself can still change).
What does seen[key] return for a missing key?
null. The [] operator on a Map<int, int> returns int?, and after if (j != null) Dart promotes j to int inside the branch.
Can I use containsKey instead?
seen.containsKey(key) followed by seen[key]! works but looks the key up twice. A single lookup with a null check does both at once.
Why not nums.indexOf(target - nums[i])?
indexOf scans the list on every call, bringing the loop back to O(n²), and it can return the current index when the partner equals the number itself.
Can the sum overflow?
Dart int is 64-bit on the VM, so sums of values within ±10⁹ cannot overflow. On the web, compiled to JavaScript, integers are exact up to 2⁵³.
FAQ for Scala
Why boundary and break instead of return?
A for loop body is a closure, so return inside it is a non-local return, which Scala 3 deprecates with a warning. boundary { ... } with break(value) is the Scala 3 replacement: break leaves the boundary and makes value its result.
Why a mutable HashMap instead of an immutable Map?
The map grows by one entry per number. A mutable.HashMap updates in place; an immutable Map would create a new map on every insert, which is slower for a hot loop like this one.
Why Array.empty[Int] for no pair?
It returns an empty Array[Int] with its element type spelled out. A bare Array() also compiles here because the return type tells Scala the element type.
Why nums(i) and not nums[i]?
Scala uses square brackets for type parameters, so arrays are indexed with parentheses. nums(i) is sugar for nums.apply(i).
Can the sum overflow?
An Int holds about ±2.1 × 10⁹. The constraints keep values within ±10⁹, so target - nums(i) fits. For wider values use Long.
Keep going
Valid Parentheses2 / 40 · Stack · EasyTry Two Sum in Python (Two Sum in Python), JavaScript (Two Sum in JavaScript), Java (Two Sum in Java), Go (Two Sum in Go), C# (Two Sum in C#), C++ (Two Sum in C++), C (Two Sum in C), Kotlin (Two Sum in Kotlin), Rust (Two Sum in Rust), Swift (Two Sum in Swift), PHP (Two Sum in PHP), Ruby (Two Sum in Ruby), Dart (Two Sum in Dart) or Scala (Two Sum in Scala) instead.