4Sum extends Two Sum and 3Sum: instead of pairs or triplets, we need all unique groups of four numbers that add up to a given target.
The uniqueness requirement drives most of the implementation work. If the array contains duplicates, we must not return the same quadruplet twice. Given [2, 2, 2, 2, 2] with target 8, the answer is [[2, 2, 2, 2]], not five copies of it.
The challenge is twofold: find all valid quadruplets efficiently, and produce each one exactly once. A hash set can deduplicate results after the fact, but sorting the array up front lets us skip duplicates during enumeration and enables a two-pointer scan for the innermost pair.
1 <= nums.length <= 200: With n at most 200, O(n^3) is at most 8 million operations, well within limits. The O(n^4) brute force checks C(200, 4), about 65 million index combinations, and the hashing it needs for deduplication pushes it past typical time limits.-10^9 <= nums[i] <= 10^9: Values are large, so the sum of four numbers can overflow a 32-bit integer. We need to use long (or equivalent) when computing sums.-10^9 <= target <= 10^9: The target can be negative, so our solution must handle negative numbers and negative targets correctly.Check every combination of four distinct indices, compute each sum, and compare it against the target. Matches go into a set so that duplicate quadruplets collapse to one entry.
Four nested loops implement this directly, at the cost of examining every combination whether or not it has any chance of matching.
(i, j, k, l) where i < j < k < l.nums[i] + nums[j] + nums[k] + nums[l] == target.Loading animation...
The bottleneck is the innermost loop scanning linearly for the fourth number. Since the array is sorted, we can replace the two innermost loops with a two-pointer scan that finds pairs in O(n) instead of O(n^2).
The problem reduces layer by layer. 4Sum is "fix one element, solve 3Sum on the rest." 3Sum is "fix one element, solve Two Sum on the rest." Two Sum on a sorted array is a two-pointer scan.
So the structure is: sort the array, fix the first two numbers with nested loops, then move two pointers (left and right) toward each other to find the remaining pair. Sorting serves two purposes: it enables the two-pointer scan, and it places equal values next to each other so duplicates can be skipped with a single comparison.
One implementation detail: the sum of four numbers, each up to 10^9 in absolute value, can reach 4 * 10^9, which exceeds the 32-bit integer range. Compute the sum in a 64-bit type.
The two-pointer scan discards only impossible pairs. When the sum is below the target, every pair combining the current left with a smaller right sums to even less, so left can be advanced without losing a valid pair. The symmetric argument justifies moving right inward when the sum is too large.
Duplicate skipping is safe for a structural reason that repeats at every level: when nums[i] == nums[i - 1], every quadruplet starting at index i draws its remaining three elements from a subrange of what index i - 1 already searched, so it has already been recorded. The same containment argument covers the j loop and the pointer moves after a match.
i from 0 to n - 4. Skip if nums[i] equals the previous element.j from i + 1 to n - 3. Skip if nums[j] equals the previous element at this level.left = j + 1 and right = n - 1.left < right, compute the sum. If it matches, record the quadruplet and skip duplicates on both sides. If it is too small, move left right. If too large, move right left.Loading animation...
O(n^3) is the best this strategy can do asymptotically, but bound checks at each loop level can skip large parts of the search space in practice.
Two checks at each loop level cut the work. At the i level: if nums[i] plus the three elements right after it already exceeds the target, every later choice of i has an even larger minimum because the array is sorted, so we can break out of the loop entirely instead of continuing. If nums[i] plus the three largest elements still falls short of the target, this i is too small but a later one might not be, so we continue to the next iteration. The same pair of checks runs inside the j loop with nums[i] held fixed.
These four pruning lines leave the asymptotic bound unchanged but skip entire branches of the search on most inputs.
i from 0 to n - 4. Skip duplicates. Apply min/max sum pruning.j from i + 1 to n - 3. Skip duplicates. Apply min/max sum pruning.left and right to find the remaining pair, same as Approach 2.Loading animation...
Every version so far hardcodes four levels of iteration. The same reduction works for any k, and writing it recursively produces one solution that covers 2Sum, 3Sum, 4Sum, and beyond.
Approach 2 reduced 4Sum to 3Sum and 3Sum to Two Sum by hand, one nested loop per layer. The reduction is identical at every layer, so it can be written once as a recursive function: kSum(start, target, k) fixes one element and delegates the rest to kSum(start + 1, target - element, k - 1). When k reaches 2, the sorted two-pointer scan takes over as the base case.
Deduplication carries over unchanged. Each recursion level skips elements equal to their predecessor, and the base case skips repeated pair values, so every unique quadruplet is produced once.
The payoff is generality: the same function solves 5Sum or 6Sum by changing the initial k. Passing the running target down as a 64-bit value also handles overflow in one place, since each subtraction can push the remaining target outside the 32-bit range.
kSum(start = 0, target, k = 4).k == 2, run the two-pointer scan from start to the end of the array and return all unique pairs that sum to the remaining target.i from start to n - k, skipping nums[i] when i > start and nums[i] equals nums[i - 1].kSum(i + 1, target - nums[i], k - 1) and prepend nums[i] to every tuple the call returns.Loading animation...