18. 4Sum - Explanation

Problem Link

Description

You are given an integer array nums of size n, return an array of all the unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that:

  • 0 <= a, b, c, d < n
  • a, b, c, and d are distinct.
  • nums[a] + nums[b] + nums[c] + nums[d] == target

You may return the answer in any order.

Note: [1,0,3,2] and [3,0,1,2] are considered as same quadruplets.

Example 1:

Input: nums = [3,2,3,-3,1,0], target = 3

Output: [[-3,0,3,3],[-3,1,2,3]]

Example 2:

Input: nums = [1,-1,1,-1,1,-1], target = 2

Output: [[-1,1,1,1]]

Constraints:

  • 1 <= nums.length <= 200
  • -1,000,000,000 <= nums[i] <= 1,000,000,000
  • -1,000,000,000 <= target <= 1,000,000,000


Topics

Company Tags

Please upgrade to NeetCode Pro to view company tags.



Prerequisites

Before attempting this problem, you should be comfortable with:

  • Two Pointers Technique - Using two pointers moving inward on a sorted array to find pairs with a target sum
  • Sorting - The two-pointer approach requires the array to be sorted first
  • Handling Duplicates - Skipping duplicate elements to avoid producing duplicate results
  • Two Sum and Three Sum Problems - 4Sum builds directly on the patterns from these simpler problems

1. Brute Force

Intuition

The simplest approach is to try all possible combinations of four distinct elements and check if their sum equals the target. To avoid duplicate quadruplets, we first sort the array and use a set to store unique results. While this guarantees correctness, checking every combination of four elements is extremely slow for larger inputs.

Algorithm

  1. Sort the array.
  2. Use four nested loops to iterate through all combinations of indices a < b < c < d.
  3. For each combination, check if nums[a] + nums[b] + nums[c] + nums[d] == target.
  4. If true, add the quadruplet to a set (to handle duplicates automatically).
  5. Convert the set to a list and return.
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        n = len(nums)
        nums.sort()
        res = set()

        for a in range(n):
            for b in range(a + 1, n):
                for c in range(b + 1, n):
                    for d in range(c + 1, n):
                        if nums[a] + nums[b] + nums[c] + nums[d] == target:
                            res.add((nums[a], nums[b], nums[c], nums[d]))
        return list(res)

Time & Space Complexity

  • Time complexity: O(n4)O(n ^ 4)
  • Space complexity: O(m)O(m)

Where nn is the size of the array numsnums and mm is the number of quadruplets.


2. Hash Map

Intuition

We can reduce one loop by using a hash map. After sorting, we fix the first three elements with nested loops and use the hash map to check if the fourth element exists. The hash map stores the count of each number, and we decrement counts as we iterate to avoid using the same element twice. This reduces complexity from O(n^4) to O(n^3) while still handling duplicates by skipping consecutive equal elements.

Algorithm

  1. Sort the array and build a frequency map of all elements.
  2. For each index i, decrement its count and skip duplicates.
  3. For each index j > i, decrement its count and skip duplicates.
  4. For each index k > j, decrement its count and skip duplicates.
  5. Calculate fourth = target - nums[i] - nums[j] - nums[k].
  6. If fourth exists in the map with count > 0, add the quadruplet.
  7. Restore counts after processing each loop level.
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        count = defaultdict(int)
        for num in nums:
            count[num] += 1

        res = []
        for i in range(len(nums)):
            count[nums[i]] -= 1
            if i > 0 and nums[i] == nums[i - 1]:
                continue

            for j in range(i + 1, len(nums)):
                count[nums[j]] -= 1
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue

                for k in range(j + 1, len(nums)):
                    count[nums[k]] -= 1
                    if k > j + 1 and nums[k] == nums[k - 1]:
                        continue

                    fourth = target - (nums[i] + nums[j] + nums[k])
                    if count[fourth] > 0:
                        res.append([nums[i], nums[j], nums[k], fourth])

                for k in range(j + 1, len(nums)):
                    count[nums[k]] += 1

            for j in range(i + 1, len(nums)):
                count[nums[j]] += 1

        return res

Time & Space Complexity

  • Time complexity: O(n3)O(n ^ 3)
  • Space complexity:
    • O(n)O(n) space for the hash map.
    • O(m)O(m) space for the output array.

Where nn is the size of the array numsnums and mm is the number of quadruplets.


3. Two Pointers

Intuition

The two-pointer technique from 2Sum and 3Sum extends naturally to 4Sum. After sorting, we fix the first two elements with nested loops, then use two pointers to find pairs that complete the target sum. The left pointer starts just after the second fixed element, and the right pointer starts at the end. We move them inward based on whether the current sum is too small or too large. Skipping duplicates at each level ensures unique quadruplets.

Algorithm

  1. Sort the array.
  2. Iterate i from 0 to n, skipping duplicates.
  3. For each i, iterate j from i + 1 to n, skipping duplicates.
  4. Use two pointers: left = j + 1 and right = n - 1.
  5. While left < right:
    • If sum equals target, add quadruplet and move both pointers while skipping duplicates.
    • If sum is less than target, increment left.
    • If sum is greater than target, decrement right.
  6. Return the result list.
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        n = len(nums)
        res = []

        for i in range(n):
            if i > 0 and nums[i] == nums[i - 1]:
                continue
            for j in range(i + 1, n):
                if j > i + 1 and nums[j] == nums[j - 1]:
                    continue
                left, right = j + 1, n - 1
                while left < right:
                    total = nums[i] + nums[j] + nums[left] + nums[right]
                    if total == target:
                        res.append([nums[i], nums[j], nums[left], nums[right]])
                        left += 1
                        right -= 1
                        while left < right and nums[left] == nums[left - 1]:
                            left += 1
                        while left < right and nums[right] == nums[right + 1]:
                            right -= 1
                    elif total < target:
                        left += 1
                    else:
                        right -= 1

        return res

Time & Space Complexity

  • Time complexity: O(n3)O(n ^ 3)
  • Space complexity:
    • O(1)O(1) or O(n)O(n) space depending on the sorting algorithm.
    • O(m)O(m) space for the output array.

4. K-Sum + Two Pointers

Intuition

We can generalize the approach to solve K-Sum for any K using recursion. The idea is to reduce the problem: for K > 2, we fix one element and recursively solve (K-1)-Sum. When K reaches 2, we use the two-pointer technique as the base case. This approach is flexible and can handle any value of K without changing the core logic.

Algorithm

  1. Sort the array.
  2. Define a recursive function kSum(k, start, target):
    • Base case (k == 2): Use two pointers from start to find pairs summing to target.
    • Recursive case: For each element at index i from start, recursively call kSum(k - 1, i + 1, target - nums[i]).
    • Skip duplicates at each recursion level.
    • Track the current partial solution in a list, adding/removing elements as we recurse.
  3. Call kSum(4, 0, target) and return the collected results.
class Solution:
    def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
        nums.sort()
        res, quad = [], []

        def kSum(k, start, target):
            if k == 2:
                l, r = start, len(nums) - 1
                while l < r:
                    if nums[l] + nums[r] < target:
                        l += 1
                    elif nums[l] + nums[r] > target:
                        r -= 1
                    else:
                        res.append(quad + [nums[l], nums[r]])
                        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 -= 1
                return

            for i in range(start, len(nums) - k + 1):
                if i > start and nums[i] == nums[i - 1]:
                    continue
                quad.append(nums[i])
                kSum(k - 1, i + 1, target - nums[i])
                quad.pop()

        kSum(4, 0, target)
        return res

Time & Space Complexity

  • Time complexity: O(n3)O(n ^ 3)
  • Space complexity:
    • O(1)O(1) or O(n)O(n) space depending on the sorting algorithm.
    • O(m)O(m) space for the output array.

Common Pitfalls

Integer Overflow

When summing four integers, the result can exceed the 32-bit integer range. Always use a 64-bit type (like long in Java/C++) for the sum calculation to avoid overflow.

// Wrong: may overflow
int sum = nums[i] + nums[j] + nums[left] + nums[right];
// Correct: use long to prevent overflow
long sum = (long)nums[i] + nums[j] + nums[left] + nums[right];

Incorrect Duplicate Skipping

When skipping duplicates, you must check against the previous element, not the next one. Also, the skip should only happen after processing the first occurrence, not before.

# Wrong: skips before processing first occurrence
if i > 0 and nums[i] == nums[i - 1]:  # This is correct for outer loops
# Wrong for two-pointer: checking next instead of previous
while left < right and nums[left] == nums[left + 1]:
# Correct: skip after finding a match, check previous
while left < right and nums[left] == nums[left - 1]:

Forgetting to Sort the Array

The two-pointer technique only works on a sorted array. Forgetting to sort first will cause the algorithm to miss valid quadruplets or produce incorrect results.