454. 4Sum II
Learn how to solve the 4Sum II problem efficiently with a hashing approach. By precomputing sums of two arrays and using a dictionary for lookups, reduce the time complexity. Optimize your solution and scale with ease!
Problem Definition
LeetCode: 454. 4Sum II
Given four integer arrays nums1
, nums2
, nums3
, and nums4
all of length n
, return the number of tuples (i, j, k, l)
such that:
0 <= i, j, k, l < n
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
Example 1:
Input: nums1 = [1,2], nums2 = [-2,-1], nums3 = [-1,2], nums4 = [0,2]
Output: 2
Explanation:
The two tuples are:
1. (0, 0, 0, 1) -> nums1[0] + nums2[0] + nums3[0] + nums4[1] = 1 + (-2) + (-1) + 2 = 0
2. (1, 1, 0, 0) -> nums1[1] + nums2[1] + nums3[0] + nums4[0] = 2 + (-1) + (-1) + 0 = 0
Example 2:
Input: nums1 = [0], nums2 = [0], nums3 = [0], nums4 = [0]
Output: 1
Constraints:
n == nums1.length
n == nums2.length
n == nums3.length
n == nums4.length
1 <= n <= 200
-228 <= nums1[i], nums2[i], nums3[i], nums4[i] <= 228
Thought Process
At first glance, this problem might remind you of the classic 3Sum or 4Sum problems, where you search for combinations within a single array that sum to a target. However, there are some critical differences here:
- We have four separate arrays, and we need to pick one element from each.
- Unlike the classic 4Sum problem (from a single array), we don’t need to worry about avoiding duplicate quadruples in the same way, because each quadruple is formed from choosing one element from each distinct array. This simplifies the problem substantially.
- Using a brute force approach—checking all
O(n^4)
combinations—would be too slow for largen
.
Key Insight: We can use a hash map (dictionary) to store the sums of two arrays, and then find complement sums from the other two arrays. Specifically:
- Pick two arrays (for instance
nums1
andnums2
) and compute all possible sumsn1 + n2
, storing their frequencies in a dictionary. This takesO(n^2)
time. - For the remaining two arrays (
nums3
andnums4
), for each combinationn3 + n4
, check if-(n3 + n4)
is in the dictionary. If it is, we add its frequency to our count. - The dictionary lookups are
O(1)
on average, so this approach significantly reduces complexity fromO(n^4)
toO(n^2)
.
Why Hashing Works Well Here:
- By precomputing and hashing the sums of
nums1
andnums2
, we handle half of the problem inO(n^2)
time. - Then, as we iterate through
nums3
andnums4
, a single dictionary lookup tells us how many pairs from the first two arrays can complement the current pair to sum to zero.
Solution 1: Using a Dictionary (Hash Map) for Two-Array Sums
How It Works
- Create a dictionary (
hashmap
) to store sums of elements fromnums1
andnums2
.- Key: sum of
nums1[i] + nums2[j]
- Value: count of how many such pairs produce this sum.
- Key: sum of
- Iterate over every pair
(n1, n2)
innums1
andnums2
, updatehashmap[n1+n2]
with the frequency. - Initialize a count variable to 0.
- For every pair
(n3, n4)
innums3
andnums4
, computekey = -(n3+n4)
.- If
key
exists inhashmap
, addhashmap[key]
tocount
.
- If
- Return
count
.
Code Explanation
class Solution:
def fourSumCount(self, nums1, nums2, nums3, nums4):
# Dictionary to store sum frequencies of nums1 and nums2
hashmap = dict()
# Compute all sums of elements from nums1 and nums2
for n1 in nums1:
for n2 in nums2:
s = n1 + n2
hashmap[s] = hashmap.get(s, 0) + 1
# Count how many combinations form zero with nums3 and nums4
count = 0
for n3 in nums3:
for n4 in nums4:
complement = - (n3 + n4)
if complement in hashmap:
count += hashmap[complement]
return count
Step-by-Step:
- Build
hashmap
for the sums ofnums1
andnums2
. - For each
(n3, n4)
sum, look up-(n3+n4)
inhashmap
. - Accumulate the count based on frequencies found.
Complexity Analysis
- Time Complexity: O(n^2), since we generate
n^2
pairs fromnums1
andnums2
, andn^2
pairs fromnums3
andnums4
. - Space Complexity: O(n^2) for the dictionary that stores sums of
nums1
andnums2
.
Solution 2: Using defaultdict
for Cleaner Code
We can simplify dictionary handling further using collections.defaultdict
to avoid manual checks.
Code Explanation
from collections import defaultdict
class Solution:
def fourSumCount(self, nums1, nums2, nums3, nums4):
rec = defaultdict(int)
# Build frequency map of sums from nums1 and nums2
for i in nums1:
for j in nums2:
rec[i+j] += 1
# Count how many tuples result in zero sum
count = 0
for i in nums3:
for j in nums4:
count += rec.get(-(i+j), 0)
return count
Step-by-Step:
rec
is a defaultdict(int), so any new key starts at 0 automatically.- Increase
rec[i+j]
for all combinations fromnums1
andnums2
. - For each
(i, j)
fromnums3
andnums4
, addrec[-(i+j)]
tocount
if it exists.
Complexity Analysis:
- Time Complexity: O(n^2)
- Space Complexity: O(n^2)
Key Takeaways
- Hashing Reduces Complexity: Converting a 4-array sum problem from O(n^4) to O(n^2) is achieved by using a hash map to store partial sums.
- No Need for Complicated Duplicate Removal: Unlike the classic 4Sum problem from a single array, we don’t need to remove duplicates here because each tuple is inherently unique by its four indices chosen from four separate arrays.
- Scalability: The hashing strategy makes it feasible to handle larger input sizes efficiently.
- Readability and Pythonic Code: Using
defaultdict
andget()
methods makes the solution concise and clearer.
Discussion