🧠
Hash Map Based Pair Sum + Two Sum
💡 Using a hash map to store pair sums can reduce the problem to finding two pairs that sum to target, but requires careful duplicate handling.
Intuition
Precompute all pairs and their sums, then find two pairs whose sums add to target without overlapping indices.
Algorithm
- Create a hash map mapping pair sums to list of index pairs.
- Iterate over all pairs and store their sums in the map.
- For each sum, check if target - sum exists in the map.
- Combine pairs from both sums ensuring indices do not overlap and add unique quadruplets.
💡 The main challenge is avoiding index overlap and duplicates while combining pairs.
from typing import List
from collections import defaultdict
def fourSum(nums: List[int], target: int) -> List[List[int]]:
nums.sort()
n = len(nums)
two_sum_map = defaultdict(list)
res = set()
for i in range(n):
for j in range(i + 1, n):
two_sum_map[nums[i] + nums[j]].append((i, j))
for sum1 in two_sum_map:
sum2 = target - sum1
if sum2 not in two_sum_map:
continue
list1 = two_sum_map[sum1]
list2 = two_sum_map[sum2]
for (i, j) in list1:
for (k, l) in list2:
if i != k and i != l and j != k and j != l:
quad = tuple(sorted([nums[i], nums[j], nums[k], nums[l]]))
res.add(quad)
return [list(quad) for quad in sorted(res)]
# Driver code
if __name__ == '__main__':
print(fourSum([1,0,-1,0,-2,2], 0))
Line Notes
nums.sort()Sort to keep quadruplets ordered and simplify duplicate handling.
two_sum_map = defaultdict(list)Map sums to pairs of indices for quick lookup.
if i != k and i != l and j != k and j != l:Ensure pairs do not share indices to form valid quadruplets.
res.add(quad)Use a set to avoid duplicate quadruplets.
import java.util.*;
public class FourSumHashMap {
public static List<List<Integer>> fourSum(int[] nums, int target) {
Arrays.sort(nums);
int n = nums.length;
Map<Integer, List<int[]>> twoSumMap = new HashMap<>();
Set<List<Integer>> res = new HashSet<>();
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
int sum = nums[i] + nums[j];
twoSumMap.computeIfAbsent(sum, k -> new ArrayList<>()).add(new int[]{i, j});
}
}
for (int sum1 : twoSumMap.keySet()) {
int sum2 = target - sum1;
if (!twoSumMap.containsKey(sum2)) continue;
List<int[]> list1 = twoSumMap.get(sum1);
List<int[]> list2 = twoSumMap.get(sum2);
for (int[] pair1 : list1) {
for (int[] pair2 : list2) {
int i = pair1[0], j = pair1[1], k = pair2[0], l = pair2[1];
if (i != k && i != l && j != k && j != l) {
int[] quadArr = new int[]{nums[i], nums[j], nums[k], nums[l]};
Arrays.sort(quadArr);
List<Integer> quad = Arrays.asList(quadArr[0], quadArr[1], quadArr[2], quadArr[3]);
res.add(quad);
}
}
}
}
List<List<Integer>> result = new ArrayList<>(res);
result.sort((a,b) -> {
for (int i = 0; i < 4; i++) {
if (!a.get(i).equals(b.get(i))) return a.get(i) - b.get(i);
}
return 0;
});
return result;
}
public static void main(String[] args) {
System.out.println(fourSum(new int[]{1,0,-1,0,-2,2}, 0));
}
}
Line Notes
Arrays.sort(nums);Sort array to keep quadruplets ordered and simplify duplicates.
twoSumMap.computeIfAbsent(sum, k -> new ArrayList<>()).add(new int[]{i, j});Store pairs by their sum for quick lookup.
if (i != k && i != l && j != k && j != l)Ensure pairs do not share indices to form valid quadruplets.
res.add(quad);Use a set to avoid duplicate quadruplets.
#include <iostream>
#include <vector>
#include <algorithm>
#include <unordered_map>
#include <set>
using namespace std;
vector<vector<int>> fourSum(vector<int>& nums, int target) {
sort(nums.begin(), nums.end());
int n = nums.size();
unordered_map<int, vector<pair<int,int>>> twoSumMap;
set<vector<int>> res;
for (int i = 0; i < n; i++) {
for (int j = i + 1; j < n; j++) {
twoSumMap[nums[i] + nums[j]].emplace_back(i, j);
}
}
for (auto& [sum1, list1] : twoSumMap) {
int sum2 = target - sum1;
if (twoSumMap.find(sum2) == twoSumMap.end()) continue;
auto& list2 = twoSumMap[sum2];
for (auto& p1 : list1) {
for (auto& p2 : list2) {
int i = p1.first, j = p1.second, k = p2.first, l = p2.second;
if (i != k && i != l && j != k && j != l) {
vector<int> quad = {nums[i], nums[j], nums[k], nums[l]};
sort(quad.begin(), quad.end());
res.insert(quad);
}
}
}
}
return vector<vector<int>>(res.begin(), res.end());
}
int main() {
vector<int> nums = {1,0,-1,0,-2,2};
vector<vector<int>> result = fourSum(nums, 0);
for (auto &quad : result) {
for (int num : quad) cout << num << ' ';
cout << '\n';
}
return 0;
}
Line Notes
sort(nums.begin(), nums.end());Sort array to keep quadruplets ordered and simplify duplicates.
twoSumMap[nums[i] + nums[j]].emplace_back(i, j);Store pairs by their sum for quick lookup.
if (i != k && i != l && j != k && j != l)Ensure pairs do not share indices to form valid quadruplets.
res.insert(quad);Use a set to avoid duplicate quadruplets.
function fourSum(nums, target) {
nums.sort((a,b) => a - b);
const twoSumMap = new Map();
const res = new Set();
const n = nums.length;
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
const sum = nums[i] + nums[j];
if (!twoSumMap.has(sum)) twoSumMap.set(sum, []);
twoSumMap.get(sum).push([i, j]);
}
}
for (let [sum1, list1] of twoSumMap.entries()) {
const sum2 = target - sum1;
if (!twoSumMap.has(sum2)) continue;
const list2 = twoSumMap.get(sum2);
for (let [i, j] of list1) {
for (let [k, l] of list2) {
if (i !== k && i !== l && j !== k && j !== l) {
const quad = [nums[i], nums[j], nums[k], nums[l]].sort((a,b) => a - b);
res.add(quad.join(','));
}
}
}
}
return Array.from(res).map(s => s.split(',').map(Number));
}
// Test
console.log(fourSum([1,0,-1,0,-2,2], 0));
Line Notes
nums.sort((a,b) => a - b);Sort array to keep quadruplets ordered and simplify duplicates.
twoSumMap.get(sum).push([i, j]);Store pairs by their sum for quick lookup.
if (i !== k && i !== l && j !== k && j !== l)Ensure pairs do not share indices to form valid quadruplets.
res.add(quad.join(','));Use a set of strings to avoid duplicate quadruplets.
TimeO(n^2 + k^2) where k is average pairs per sum
SpaceO(n^2) for storing pairs
Computing all pairs is O(n^2), combining pairs can be up to O(n^4) worst but usually less due to pruning.
💡 This approach uses more memory but can be faster for smaller inputs; for large n it may be impractical.
Interview Verdict: Accepted but less practical for large inputs
Good to know as an alternative approach, but space and complexity can explode for large arrays.