LeetCode 454 - 4Sum II
The problem gives four integer arrays, nums1, nums2, nums3, and nums4, each containing n elements. We must count how many index tuples (i, j, k, l) satisfy: The important detail is that we are counting tuples of indices, not unique value combinations.
Difficulty: 🟡 Medium
Topics: Array, Hash Table
Solution
Problem Understanding
The problem gives four integer arrays, nums1, nums2, nums3, and nums4, each containing n elements. We must count how many index tuples (i, j, k, l) satisfy:
nums1[i] + nums2[j] + nums3[k] + nums4[l] == 0
The important detail is that we are counting tuples of indices, not unique value combinations. If the same value appears multiple times in an array, every valid index combination contributes separately to the answer.
A direct interpretation is that we must examine all possible ways to pick one element from each array and determine whether their sum equals zero. Since each array has length n, the total number of combinations is:
n * n * n * n = n^4
The constraints tell us that:
1 <= n <= 200
If we attempted all 200^4 = 1,600,000,000 combinations, the solution would be far too slow. This immediately suggests that a brute-force approach is not feasible.
The values in the arrays can be negative, positive, or zero. This matters because there is no ordering or monotonic behavior we can exploit with sorting and two pointers directly across four arrays. Instead, the problem strongly suggests using hashing to store partial sums.
Several edge cases are important:
- Arrays may contain duplicate values, which means frequencies matter.
- Arrays may contain all zeros, which creates many valid tuples.
- Negative numbers and positive numbers can cancel each other out in many ways.
- Multiple different pairs may produce the same sum, so counting frequencies correctly is essential.
- The answer can become large because many combinations may be valid.
Approaches
Brute Force Approach
The most straightforward solution is to try every possible tuple (i, j, k, l).
We use four nested loops:
- The first loop chooses an element from
nums1 - The second loop chooses an element from
nums2 - The third loop chooses an element from
nums3 - The fourth loop chooses an element from
nums4
For every combination, we compute:
nums1[i] + nums2[j] + nums3[k] + nums4[l]
If the sum equals zero, we increment the answer.
This approach is correct because it explicitly checks every possible tuple. However, it is computationally expensive. With n = 200, the algorithm requires up to 200^4 operations, which is far too large for practical execution.
Optimal Approach
The key observation is that the equation:
a + b + c + d = 0
can be rearranged as:
a + b = -(c + d)
Instead of considering four numbers at once, we can split the problem into two independent pair-sum problems.
We first compute all possible sums from nums1 and nums2 and store how many times each sum occurs in a hash map.
Then, for every possible sum from nums3 and nums4, we check whether its negation exists in the hash map.
For example:
If nums3[k] + nums4[l] = 5
Then we need a pair from nums1 and nums2 whose sum is -5
This reduces the complexity dramatically:
- Computing pair sums takes
O(n^2) - Looking up complements also takes
O(n^2)
The total becomes O(n^2) instead of O(n^4).
Approach Comparison
| Approach | Time Complexity | Space Complexity | Notes |
|---|---|---|---|
| Brute Force | O(n^4) | O(1) | Checks every possible tuple directly |
| Optimal | O(n^2) | O(n^2) | Uses a hash map to store pair sums |
Algorithm Walkthrough
- Create a hash map called
pair_sum_count.
The keys will be sums formed by elements from nums1 and nums2. The values will represent how many times each sum appears.
2. Iterate through every element in nums1.
For each element, iterate through every element in nums2.
3. Compute the pair sum.
For every pair (a, b):
current_sum = a + b
Increment the frequency of this sum in the hash map.
4. Initialize a variable called result to zero.
This variable will store the total number of valid tuples.
5. Iterate through every element in nums3.
For each element, iterate through every element in nums4.
6. Compute the current sum from the second pair.
current_sum = c + d
- Find the complementary sum.
To satisfy:
a + b + c + d = 0
we need:
a + b = -(c + d)
So we compute:
target = -current_sum
- Check the hash map.
If target exists in the hash map, add its frequency to result.
This works because every occurrence of target represents a valid pair from the first two arrays.
9. Return result.
Why it works
The algorithm works because every valid tuple can be uniquely decomposed into:
(nums1[i] + nums2[j]) + (nums3[k] + nums4[l]) = 0
The hash map stores exactly how many ways the first half of the equation can produce a given sum. For every second-half pair sum, we look for the exact complementary value needed to make the total zero. Since all pair combinations are considered, every valid tuple is counted exactly once.
Python Solution
from collections import defaultdict
from typing import List
class Solution:
def fourSumCount(
self,
nums1: List[int],
nums2: List[int],
nums3: List[int],
nums4: List[int]
) -> int:
pair_sum_count = defaultdict(int)
# Store all sums from nums1 and nums2
for num1 in nums1:
for num2 in nums2:
pair_sum_count[num1 + num2] += 1
result = 0
# Find complementary sums from nums3 and nums4
for num3 in nums3:
for num4 in nums4:
target = -(num3 + num4)
if target in pair_sum_count:
result += pair_sum_count[target]
return result
The implementation begins by creating a hash map using defaultdict(int). This allows frequencies to be incremented easily without checking whether a key already exists.
The first nested loop generates all possible sums from nums1 and nums2. Each sum is stored along with its frequency. If the same sum appears multiple times, the count increases accordingly.
The second nested loop processes all pair sums from nums3 and nums4. For every pair, the algorithm computes the complementary value needed to make the total sum equal zero.
If this complementary value exists in the hash map, its frequency is added to the result. This automatically counts all matching tuples formed by the stored pairs.
Finally, the algorithm returns the total count of valid tuples.
Go Solution
func fourSumCount(nums1 []int, nums2 []int, nums3 []int, nums4 []int) int {
pairSumCount := make(map[int]int)
// Store all sums from nums1 and nums2
for _, num1 := range nums1 {
for _, num2 := range nums2 {
pairSumCount[num1+num2]++
}
}
result := 0
// Find complementary sums from nums3 and nums4
for _, num3 := range nums3 {
for _, num4 := range nums4 {
target := -(num3 + num4)
if count, exists := pairSumCount[target]; exists {
result += count
}
}
}
return result
}
The Go implementation follows the same logic as the Python version. The main difference is that Go uses an explicit map[int]int instead of defaultdict.
When checking for a complement, Go uses the two-value map lookup syntax:
count, exists := pairSumCount[target]
This safely determines whether the key exists before using the value.
The constraints are small enough that integer overflow is not a concern in Go for standard int types.
Worked Examples
Example 1
nums1 = [1,2]
nums2 = [-2,-1]
nums3 = [-1,2]
nums4 = [0,2]
Step 1: Build Pair Sums from nums1 and nums2
| num1 | num2 | Sum | Hash Map State |
|---|---|---|---|
| 1 | -2 | -1 | {-1: 1} |
| 1 | -1 | 0 | {-1: 1, 0: 1} |
| 2 | -2 | 0 | {-1: 1, 0: 2} |
| 2 | -1 | 1 | {-1: 1, 0: 2, 1: 1} |
Final hash map:
{
-1: 1,
0: 2,
1: 1
}
Step 2: Process nums3 and nums4
| num3 | num4 | Sum | Target | Matches in Map | Result |
|---|---|---|---|---|---|
| -1 | 0 | -1 | 1 | 1 | 1 |
| -1 | 2 | 1 | -1 | 1 | 2 |
| 2 | 0 | 2 | -2 | 0 | 2 |
| 2 | 2 | 4 | -4 | 0 | 2 |
Final answer:
2
Example 2
nums1 = [0]
nums2 = [0]
nums3 = [0]
nums4 = [0]
Step 1: Build Pair Sums
| num1 | num2 | Sum | Hash Map State |
|---|---|---|---|
| 0 | 0 | 0 | {0: 1} |
Step 2: Process Remaining Pairs
| num3 | num4 | Sum | Target | Matches | Result |
|---|---|---|---|---|---|
| 0 | 0 | 0 | 0 | 1 | 1 |
Final answer:
1
Complexity Analysis
| Measure | Complexity | Explanation |
|---|---|---|
| Time | O(n^2) | Two nested loops for pair sums and another two nested loops for complement checks |
| Space | O(n^2) | The hash map may store up to all distinct pair sums |
The algorithm computes all pair sums from the first two arrays and all pair sums from the second two arrays. Each phase requires n^2 operations, resulting in overall O(n^2) time complexity.
The hash map can contain up to n^2 distinct sums in the worst case, so the space complexity is also O(n^2).
Test Cases
from typing import List
class Solution:
def fourSumCount(
self,
nums1: List[int],
nums2: List[int],
nums3: List[int],
nums4: List[int]
) -> int:
from collections import defaultdict
pair_sum_count = defaultdict(int)
for num1 in nums1:
for num2 in nums2:
pair_sum_count[num1 + num2] += 1
result = 0
for num3 in nums3:
for num4 in nums4:
result += pair_sum_count.get(-(num3 + num4), 0)
return result
sol = Solution()
assert sol.fourSumCount(
[1, 2],
[-2, -1],
[-1, 2],
[0, 2]
) == 2 # Provided example with two valid tuples
assert sol.fourSumCount(
[0],
[0],
[0],
[0]
) == 1 # Single-element arrays with one valid tuple
assert sol.fourSumCount(
[1],
[1],
[1],
[1]
) == 0 # No possible zero sum
assert sol.fourSumCount(
[0, 0],
[0, 0],
[0, 0],
[0, 0]
) == 16 # Every combination is valid
assert sol.fourSumCount(
[-1, -1],
[1, 1],
[0, 0],
[0, 0]
) == 16 # Duplicate values create multiple tuples
assert sol.fourSumCount(
[100000000],
[-100000000],
[0],
[0]
) == 1 # Large magnitude values
assert sol.fourSumCount(
[1, -1],
[-1, 1],
[0, 0],
[0, 0]
) == 8 # Multiple complementary pair sums
assert sol.fourSumCount(
[2],
[-2],
[3],
[-3]
) == 1 # Exact cancellation across all arrays
print("All test cases passed.")
Test Summary
| Test | Why |
|---|---|
| Provided example 1 | Verifies standard functionality |
| Provided example 2 | Verifies minimal input size |
| All positive numbers | Ensures zero is not falsely counted |
| All zeros | Tests maximum duplicate tuple generation |
| Duplicate values | Verifies frequency counting correctness |
| Large magnitude values | Confirms handling of large integers |
| Mixed positive and negative | Tests multiple complement matches |
| Exact cancellation | Verifies basic correctness |
Edge Cases
One important edge case occurs when all arrays contain only zeros. In this scenario, every possible tuple is valid because the sum is always zero. A buggy implementation might accidentally count only unique sums instead of all index combinations. The hash map frequency approach handles this correctly because it stores how many times each pair sum occurs, not just whether it exists.
Another critical edge case involves duplicate values. For example:
nums1 = [-1, -1]
nums2 = [1, 1]
The sum 0 can be formed multiple times. A solution that stores only unique sums would undercount the answer. The implementation correctly tracks frequencies in the hash map, ensuring every valid combination contributes to the final result.
A third edge case occurs when no valid tuples exist. For example:
nums1 = [1]
nums2 = [1]
nums3 = [1]
nums4 = [1]
The total sum can never equal zero. The algorithm handles this naturally because no complementary sums are found in the hash map, so the result remains zero.
Another subtle case involves negative numbers and large magnitudes. Since the problem allows both positive and negative integers, complementary matching is essential. The implementation correctly computes:
target = -(num3 + num4)
which ensures that negative and positive sums are paired properly regardless of sign.