18. 4Sum - Explanation
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 < na, b, c,anddare 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
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
- Sort the array.
- Use four nested loops to iterate through all combinations of indices
a < b < c < d. - For each combination, check if
nums[a] + nums[b] + nums[c] + nums[d] == target. - If true, add the quadruplet to a set (to handle duplicates automatically).
- 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:
- Space complexity:
Where is the size of the array and 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
- Sort the array and build a frequency map of all elements.
- For each index
i, decrement its count and skip duplicates. - For each index
j > i, decrement its count and skip duplicates. - For each index
k > j, decrement its count and skip duplicates. - Calculate
fourth = target - nums[i] - nums[j] - nums[k]. - If
fourthexists in the map with count > 0, add the quadruplet. - 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
- space for the hash map.
- space for the output array.
Where is the size of the array and 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
- Sort the array.
- Iterate
ifrom 0 ton, skipping duplicates. - For each
i, iteratejfromi + 1ton, skipping duplicates. - Use two pointers:
left = j + 1andright = n - 1. - 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.
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
- or space depending on the sorting algorithm.
- 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
- Sort the array.
- Define a recursive function
kSum(k, start, target):- Base case (
k == 2): Use two pointers fromstartto find pairs summing to target. - Recursive case: For each element at index
ifromstart, recursively callkSum(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.
- Base case (
- 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 resTime & Space Complexity
- Time complexity:
- Space complexity:
- or space depending on the sorting algorithm.
- 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.
Sign in to join the discussion