4Sum
4Sum is LeetCode 18 (Medium), and the 4sum leetcode problem is the fourth rung of the k-sum ladder. You get an integer array nums and an integer target, and must return every unique quadruplet of values that adds up to exactly target.
- The four values come from four different positions
a,b,candd; no position is reused. - Each quadruplet is reported once. The same four values in a different order count as the same answer.
- The quadruplets, and the values inside each one, may be returned in any order.
The array holds at most 200 numbers, so an O(n³) scan is about 8 million steps and comfortable. The values reach 10⁹, so four of them can sum past the 32-bit range.
- 1 <= nums.length <= 200
- -10⁹ <= nums[i] <= 10⁹
- -10⁹ <= target <= 10⁹
Intuition
4Sum is 3Sum with one more loop in front. On a sorted array, two pointers starting at the ends find a pair with a given sum in one linear sweep: a total that is too small can only grow by moving the left pointer, and one that is too big can only shrink by moving the right.
So fix the first two values with nested loops, and the remaining two must add up to target minus those. That is one O(n) sweep per pair, giving O(n³). Sorting also puts equal values next to each other, which is what makes uniqueness a matter of stepping over neighbours rather than keeping a set.
This is the k-sum template: sort, fix k − 2 values with nested loops, and finish the innermost pair with converging pointers, for O(n^(k−1)). Two Sum on a sorted array is the base case, 3Sum adds one loop, 4Sum adds two, and each new loop needs its own duplicate skip comparing against the previous value at that level.
Approach
Before reading on, sort [1, 0, -1, 0, -2, 2] with target = 0 and run the two pointers by hand for the first pair. Then work out why [-2, 0, 0, 2] is reported once rather than twice. Aim for O(n³) time.
Sort the array
O(n log n), and it earns its cost twice over. Pointer moves now change the sum in a known direction, and equal values become neighbours, so duplicates can be skipped in place instead of filtered through a set at the end.
Fix the first two values
Loop i over the array and j from i + 1, skipping repeats:
- skip
iwhen it equalsnums[i - 1]. - skip
jwhen it equalsnums[j - 1]andj > i + 1.
Without that second guard the first j is thrown away whenever it happens to equal nums[i], losing real answers.
Close the remaining pair with two pointers
Set l = j + 1 and r = n - 1, and compare the four-value sum against target:
- below
target– movelright for a bigger value. - above
target– moverleft for a smaller one. - equal – record it, then move both ends inward.
Move both ends after a match, then skip repeats
Once a quadruplet is recorded the pair is spent, so:
- advance
land retreatrtogether – moving only one re-tests a combination already stored. - then step
lpast any value equal to the one just used, andrlikewise, or the same quadruplet comes out again.
Why no quadruplet is missed
When the sum is too small, nums[l] paired with the largest value left still falls short, so it can partner with nothing in the window and is safely discarded. The mirror argument discards nums[r] when the sum is too big, so every move drops only values that cannot be part of an answer.
Widen the sum before adding
Four values near 10⁹ exceed a signed 32-bit integer, and the wrap-around turns a large positive sum negative, which sends the pointers the wrong way. In C++ and Java cast to 64-bit before the first addition; Python's integers are unbounded.
4Sum solution in Python | C++ | Java
Common pitfalls
Skipping the second index without the j > i + 1 guard
if nums[j] == nums[j - 1]:
continueif j > i + 1 and nums[j] == nums[j - 1]:
continueOn the first turn of the inner loop nums[j - 1] is nums[i], so an equal pair is thrown away and quadruplets holding three equal values are lost. Each level's skip must only fire for repeats within that level.
Overflow on the sum in fixed-width languages
int s = nums[i] + nums[j] + nums[l] + nums[r];
long long s = (long long)nums[i] + nums[j] + nums[l] + nums[r];
Four values near 10⁹ exceed a signed 32-bit integer and wrap to a negative number, so the comparison with target sends the pointers the wrong way. The widening has to happen before the first addition, not on the result.
Moving only one pointer after recording a match
res.append([...]) l += 1
res.append([...])
l += 1
r -= 1
while l < r and nums[l] == nums[l - 1]:
l += 1
while l < r and nums[r] == nums[r + 1]:
r -= 1The current pair is used up, so both ends have to move. Advancing one alone keeps re-testing combinations already recorded, and without the skip loops equal neighbours emit the same quadruplet again.
Edge cases
[1, 2, 3]The outer loop range is empty, so no quadruplet is formed and the result is [].
[2, 2, 2, 2, 2] with target = 8One quadruplet [2, 2, 2, 2]. The skips at i and j stop the other overlapping choices being reported.
Their sum passes the 32-bit limit, so C++ and Java must add in 64-bit; Python is unaffected.
Complexity
4Sum FAQ
Can 4Sum be solved faster than O(n³)?
The 4sum python code above is the standard answer, and there is an O(n²) variant that hashes every pair sum, but deduplicating the quadruplets it produces is awkward and it costs O(n²) memory. With n capped at 200 the sorted two-pointer version is fast enough and far easier to get right.
Why must duplicates be skipped at all four positions?
Each position independently chooses a value, so a repeat at any one of them produces the same quadruplet again. Skipping only at i and j still lets the inner pointers land on equal values and emit a duplicate.