4Sum — LeetCode 18
4Sum — LeetCode 18
Find all unique quadruplets in an array that sum to a given target. Master the reduce-by-two-pointers pattern — the same intuition behind 3Sum, extended one level deeper.
Given an array nums of n integers and an integer target,
return all unique quadruplets [nums[a], nums[b], nums[c], nums[d]] such that:
• 0 <= a, b, c, d < n
• a, b, c, d are distinct indices
• nums[a] + nums[b] + nums[c] + nums[d] == target
You may return the answer in any order. The result must not contain duplicate quadruplets.
[-2, -1, 0, 0, 1, 2] target = 0
Arrays.sort(nums). This enables two-pointer convergence (sorted order guarantees that moving L right increases the sum, moving R left decreases it) and makes duplicate detection trivial — equals-previous means skip.
i — first outer loopi from 0 to n-4. If i > 0 and nums[i] == nums[i-1], skip — we've already explored all quadruplets starting with this value. This prevents duplicate quadruplets at the first position.
j — second outer loopj from i+1 to n-3. Skip duplicates with: if j > i+1 and nums[j] == nums[j-1]. Note the guard is j > i+1 not j > 0 — we only skip when it's not the very first j in this i iteration.
L and RL = j+1, R = n-1. While L < R:· sum == target → record, skip inner duplicates on both sides, advance both pointers.
· sum < target →
L++ (need bigger value).· sum > target →
R-- (need smaller value).
10⁹, four values can sum to 4×10⁹ — well beyond int range (~2.1×10⁹). Cast at least one operand to long before any addition: (long)nums[i] + nums[j] + nums[L] + nums[R].
j > 0 instead of j > i+1 for the j-duplicate skip. This incorrectly skips the very first j value after i, causing missed results.
import java.util.*; class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { Arrays.sort(nums); // O(n log n) List<List<Integer>> result = new ArrayList<>(); int n = nums.length; for (int i = 0; i < n - 3; i++) { // Skip duplicate values for i if (i > 0 && nums[i] == nums[i - 1]) continue; for (int j = i + 1; j < n - 2; j++) { // Skip duplicate values for j (guard: j > i+1, not j > 0) if (j > i + 1 && nums[j] == nums[j - 1]) continue; int left = j + 1, right = n - 1; while (left < right) { // Cast to long — prevents integer overflow long sum = (long) nums[i] + nums[j] + nums[left] + nums[right]; if (sum == target) { result.add(Arrays.asList(nums[i], nums[j], nums[left], nums[right])); // Skip duplicates for left pointer while (left < right && nums[left] == nums[left + 1]) left++; // Skip duplicates for right pointer while (left < right && nums[right] == nums[right - 1]) right--; left++; // Move both inward after recording right--; } else if (sum < target) { left++; // Sum too small → grow it } else { right--; // Sum too large → shrink it } } } } return result; } }
The two-pointer pattern extends cleanly to any K. Fix k-2 pointers recursively, then apply two-pointer at depth 2. This single function handles 2Sum, 3Sum, 4Sum — and beyond.
class Solution { public List<List<Integer>> fourSum(int[] nums, int target) { Arrays.sort(nums); return kSum(nums, (long) target, 0, 4); } private List<List<Integer>> kSum(int[] nums, long target, int start, int k) { List<List<Integer>> res = new ArrayList<>(); if (start == nums.length || k < 2) return res; if (k == 2) { // Base case: classic two-pointer int l = start, r = nums.length - 1; while (l < r) { long sum = nums[l] + nums[r]; if (sum == target) { res.add(new ArrayList<>(Arrays.asList(nums[l++], nums[r--])); } else if (sum < target) l++; else r--; while (l < r && l > start && nums[l] == nums[l-1]) l++; while (l < r && r < nums.length-1 && nums[r] == nums[r+1]) r--; } return res; } for (int i = start; i < nums.length - k + 1; i++) { if (i > start && nums[i] == nums[i - 1]) continue; for (List<Integer> subset : kSum(nums, target - nums[i], i + 1, k - 1)) { subset.0.add(0, nums[i]); // prepend current element res.add(subset); } } return res; } }
| Approach | Time | Space | Notes |
|---|---|---|---|
| Sort + Two Pointers ✓ | O(n³) | O(1) | Optimal. What LeetCode expects. Handles duplicates cleanly. |
| Fix 2 + HashMap | O(n³) | O(n) | Same time. Extra space. Dedup via Set of Lists is slower. |
| Recursive K-Sum | O(n³) | O(n) | Elegant. Generalises to any K. Slightly more call-stack overhead. |
| 4 Nested Loops | O(n⁴) | O(1) | TLE beyond n≈50. Only useful for tiny inputs. |