|
| 1 | +# 18. 4Sum |
| 2 | + |
| 3 | +**Difficulty:** Medium |
| 4 | +**Category:** Arrays, Two Pointers, Hashing |
| 5 | +**Leetcode Link:** [4Sum Problem](https://leetcode.com/problems/4sum/) |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## 📝 Introduction |
| 10 | + |
| 11 | +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: |
| 12 | + |
| 13 | +- `0 <= a, b, c, d < n` |
| 14 | +- `a`, `b`, `c`, and `d` are distinct |
| 15 | +- `nums[a] + nums[b] + nums[c] + nums[d] == target` |
| 16 | + |
| 17 | +The result must not contain duplicate quadruplets. |
| 18 | + |
| 19 | +--- |
| 20 | + |
| 21 | +## 💡 Approach & Key Insights |
| 22 | + |
| 23 | +To solve this problem, we explore different strategies from brute-force to optimal: |
| 24 | + |
| 25 | +- Start by checking every combination of four numbers (brute force). |
| 26 | +- Reduce the number of loops by leveraging hashing for lookups (better approach). |
| 27 | +- Use sorting and two pointers to achieve an optimal time complexity (final approach). |
| 28 | +- Remove duplicates using sorted input and pointer movement logic. |
| 29 | + |
| 30 | +--- |
| 31 | + |
| 32 | +## 🛠️ Breakdown of Approaches |
| 33 | + |
| 34 | +### 1️⃣ Brute Force / Naive Approach |
| 35 | + |
| 36 | +- **Explanation:** |
| 37 | + - Use four nested loops to pick all combinations of four distinct indices. |
| 38 | + - For each quadruplet, if their sum equals the target, sort it and insert it into a set (to avoid duplicates). |
| 39 | + - Finally, return all unique sorted quadruplets. |
| 40 | + |
| 41 | +- **Time Complexity:** O(N⁴ * log M) |
| 42 | + - 4 loops + set insertions (log M where M is number of unique quads) |
| 43 | + |
| 44 | +- **Space Complexity:** O(M) |
| 45 | + - To store unique quadruplets in a set. |
| 46 | + |
| 47 | +- **Example/Dry Run:** |
| 48 | + |
| 49 | +Example input: `[1, 0, -1, 0, -2, 2]`, Target = 0 |
| 50 | +Process: |
| 51 | +Check every 4-tuple → Add valid ones to set |
| 52 | +Output: `[[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]]` |
| 53 | + |
| 54 | +--- |
| 55 | + |
| 56 | +### 2️⃣ Optimized Approach (Using 3 Loops + HashSet) |
| 57 | + |
| 58 | +- **Explanation:** |
| 59 | + - Use three nested loops (`i`, `j`, `k`) to select three numbers. |
| 60 | + - Calculate the required fourth number as `target - (nums[i] + nums[j] + nums[k])`. |
| 61 | + - Search for this number in a temporary HashSet (built on-the-fly). |
| 62 | + - Sort the resulting quadruplet and store in a set to maintain uniqueness. |
| 63 | + |
| 64 | +- **Time Complexity:** O(N³ * log M) |
| 65 | + - 3 loops + HashSet lookup + set insertions |
| 66 | + |
| 67 | +- **Space Complexity:** O(M + N) |
| 68 | + - Set for results and HashSet for lookup |
| 69 | + |
| 70 | +- **Example/Dry Run:** |
| 71 | + |
| 72 | +Input: `[1, 2, -1, -2, 2, 0, -1]`, Target = 0 |
| 73 | +Step 1: i = 0, j = 1, k = 2 |
| 74 | +Sum = 1 + 2 + (-1) = 2, required = -2 |
| 75 | +Step 2: Check if -2 exists in HashSet |
| 76 | +Continue storing valid quads |
| 77 | + |
| 78 | +Output: `[[-2, -1, 1, 2], [-1, -1, 0, 2]]` |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +### 3️⃣ Best / Final Optimized Approach (2 Pointers + Sorting) |
| 83 | + |
| 84 | +- **Explanation:** |
| 85 | + - Sort the array first. |
| 86 | + - Fix two pointers (`i` and `j`) using nested loops. |
| 87 | + - Use two other pointers `k` and `l` to find the remaining two numbers using a two-pointer technique. |
| 88 | + - Skip duplicate values to maintain uniqueness. |
| 89 | + |
| 90 | +- **Time Complexity:** O(N³) |
| 91 | + - 2 fixed + 2 moving pointers = nested loops with linear scan |
| 92 | + |
| 93 | +- **Space Complexity:** O(M) |
| 94 | + - For storing results only |
| 95 | + |
| 96 | +- **Example/Dry Run:** |
| 97 | + |
| 98 | +Input: `[4, 3, 3, 4, 4, 2, 1, 2, 1, 1]`, Target = 9 |
| 99 | +Sorted: `[1, 1, 1, 2, 2, 3, 3, 4, 4, 4]` |
| 100 | +Step 1: i = 0, j = 1, k = 2, l = 9 |
| 101 | +While k < l, move based on current sum |
| 102 | +Skip duplicates after storing a valid quad |
| 103 | + |
| 104 | +Output: `[[1, 1, 3, 4], [1, 2, 2, 4], [1, 2, 3, 3]]` |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +## 📊 Complexity Analysis |
| 109 | + |
| 110 | +| Approach | Time Complexity | Space Complexity | |
| 111 | +| ------------- | --------------- | ---------------- | |
| 112 | +| Brute Force | O(N⁴ * log M) | O(M) | |
| 113 | +| Optimized | O(N³ * log M) | O(M + N) | |
| 114 | +| Best Approach | O(N³) | O(M) | |
| 115 | + |
| 116 | +--- |
| 117 | + |
| 118 | +## 📉 Optimization Ideas |
| 119 | + |
| 120 | +- Skip duplicates after sorting to avoid storing the same quadruplet multiple times. |
| 121 | +- Use long long to avoid integer overflow during summation. |
| 122 | +- Efficient pointer movement makes the solution clean and fast. |
| 123 | +- Avoid unnecessary lookups by reducing hash operations. |
| 124 | + |
| 125 | +--- |
| 126 | + |
| 127 | +## 📌 Example Walkthroughs & Dry Runs |
| 128 | + |
| 129 | +plaintext |
| 130 | +Example: |
| 131 | +Input: [1, 0, -1, 0, -2, 2], Target = 0 |
| 132 | +Sorted: [-2, -1, 0, 0, 1, 2] |
| 133 | +Step-by-step: |
| 134 | +i = 0 (-2), j = 1 (-1), k = 2 (0), l = 5 (2) |
| 135 | +Sum = -1 → too small → move k |
| 136 | +Sum = 0 → store [-2, -1, 1, 2] |
| 137 | +Skip duplicates → next iteration... |
| 138 | + |
| 139 | +Output: [[-2, -1, 1, 2], [-2, 0, 0, 2], [-1, 0, 0, 1]] |
| 140 | + |
| 141 | +--- |
| 142 | + |
| 143 | +## 🔗 Additional Resources |
| 144 | + |
| 145 | + |
| 146 | +- [GeeksForGeeks Explanation](https://www.geeksforgeeks.org/find-four-elements-that-sum-to-a-given-value-set-2/) |
| 147 | + |
| 148 | +--- |
| 149 | + |
| 150 | +Author: Abdul Wahab |
| 151 | +Date: 19/07/2025 |
0 commit comments