|
| 1 | +# 3Sum |
| 2 | +Given an integer array nums, return all the triplets [nums[i], nums[j], nums[k]] such that i != j, i != k, and j != k, and nums[i] + nums[j] + nums[k] == 0. |
| 3 | + |
| 4 | +Notice that the solution set must not contain duplicate triplets. |
| 5 | + |
| 6 | +### Constraints: |
| 7 | +0 <= nums.length <= 3000 |
| 8 | +-10^5 <= nums[i] <= 10^5 |
| 9 | + |
| 10 | +### Examples |
| 11 | +```javascript |
| 12 | +Input: nums = [-1,0,1,2,-1,-4] |
| 13 | +Output: [[-1,-1,2],[-1,0,1]] |
| 14 | +Explanation: |
| 15 | +nums[0] + nums[1] + nums[2] = -1 + 0 + 1 = 0 |
| 16 | +nums[1] + nums[2] + nums[3] = -1 + 0 + 1 = 0 |
| 17 | +There are two unique triplets that sum to zero. |
| 18 | + |
| 19 | +Input: nums = [] |
| 20 | +Output: [] |
| 21 | + |
| 22 | +Input: nums = [0] |
| 23 | +Output: [] |
| 24 | +``` |
| 25 | + |
| 26 | +## Approaches to Solve the Problem |
| 27 | +### Approach 1: Brute Force (Inefficient) |
| 28 | +##### Intuition: |
| 29 | +The brute-force solution involves checking every possible triplet in the array. We would use three nested loops to pick all combinations of three numbers and check if their sum equals zero. |
| 30 | + |
| 31 | +Steps: |
| 32 | +1. Loop through the array with three nested loops to check all possible combinations. |
| 33 | +2. For each triplet, check if the sum equals zero. |
| 34 | +3. Use a set to keep track of unique triplets to avoid duplicates. |
| 35 | +##### Time Complexity: |
| 36 | +O(n³), where n is the length of the array. This approach checks all possible triplets. |
| 37 | +##### Space Complexity: |
| 38 | +O(n) for storing unique triplets in a set. |
| 39 | +##### Python Code: |
| 40 | +```python |
| 41 | +def threeSum(nums: List[int]) -> List[List[int]]: |
| 42 | + res = set() |
| 43 | + n = len(nums) |
| 44 | + |
| 45 | + for i in range(n): |
| 46 | + for j in range(i + 1, n): |
| 47 | + for k in range(j + 1, n): |
| 48 | + if nums[i] + nums[j] + nums[k] == 0: |
| 49 | + res.add(tuple(sorted([nums[i], nums[j], nums[k]]))) |
| 50 | + |
| 51 | + return list(res) |
| 52 | +``` |
| 53 | +### Approach 2: Sorting and Two Pointers (Optimal Solution) |
| 54 | +##### Intuition: |
| 55 | +By sorting the array first, we can reduce the problem to the two-sum problem with a twist. The idea is to fix one number (nums[i]), and then use two pointers to find the other two numbers (nums[left] and nums[right]) whose sum is -nums[i]. |
| 56 | + |
| 57 | +Steps: |
| 58 | +1. Sort the array: Sorting helps in skipping duplicate triplets and allows the two-pointer technique to work efficiently. |
| 59 | +2. Iterate through the array: For each number nums[i], treat it as a fixed number. |
| 60 | +3. Two-pointer approach: After fixing nums[i], use two pointers (left and right) to find two other numbers such that their sum equals -nums[i]. |
| 61 | +4. Skip duplicates: While iterating, skip duplicate elements to avoid repeating the same triplets. |
| 62 | +##### Visualization: |
| 63 | +```perl |
| 64 | +For nums = [-1, 0, 1, 2, -1, -4], after sorting the array: |
| 65 | + |
| 66 | +Sorted array: [-4, -1, -1, 0, 1, 2] |
| 67 | + |
| 68 | +Iteration 1: |
| 69 | +Fix nums[0] = -4 |
| 70 | +left = 1, right = 5 |
| 71 | +Sum = nums[1] + nums[5] = -1 + 2 = 1 > 0 → move right left |
| 72 | + |
| 73 | +Iteration 2: |
| 74 | +Fix nums[1] = -1 |
| 75 | +left = 2, right = 5 |
| 76 | +Sum = nums[2] + nums[5] = -1 + 2 = 1 > 0 → move right left |
| 77 | +Sum = nums[2] + nums[4] = -1 + 1 = 0 → triplet found: [-1, -1, 2] |
| 78 | + |
| 79 | +Iteration 3: |
| 80 | +Fix nums[2] = -1 (skip since it’s a duplicate of nums[1]) |
| 81 | +... |
| 82 | + |
| 83 | +Continue until all triplets are checked. |
| 84 | +``` |
| 85 | +##### Time Complexity: |
| 86 | +O(n²), where n is the length of the array. Sorting the array takes O(n log n), and the two-pointer scan for each element takes O(n). |
| 87 | +##### Space Complexity: |
| 88 | +O(n) for the output list of triplets. |
| 89 | +##### Python Code: |
| 90 | +```python |
| 91 | +def threeSum(nums: List[int]) -> List[List[int]]: |
| 92 | + res = [] |
| 93 | + nums.sort() # Step 1: Sort the array |
| 94 | + |
| 95 | + for i in range(len(nums) - 2): |
| 96 | + # Step 2: Skip duplicates for the current number |
| 97 | + if i > 0 and nums[i] == nums[i - 1]: |
| 98 | + continue |
| 99 | + |
| 100 | + # Step 3: Two-pointer approach |
| 101 | + left, right = i + 1, len(nums) - 1 |
| 102 | + while left < right: |
| 103 | + total = nums[i] + nums[left] + nums[right] |
| 104 | + |
| 105 | + if total == 0: |
| 106 | + res.append([nums[i], nums[left], nums[right]]) |
| 107 | + left += 1 |
| 108 | + right -= 1 |
| 109 | + # Skip duplicates for the left and right pointers |
| 110 | + while left < right and nums[left] == nums[left - 1]: |
| 111 | + left += 1 |
| 112 | + while left < right and nums[right] == nums[right + 1]: |
| 113 | + right -= 1 |
| 114 | + elif total < 0: |
| 115 | + left += 1 # Increase the sum by moving left pointer right |
| 116 | + else: |
| 117 | + right -= 1 # Decrease the sum by moving right pointer left |
| 118 | + |
| 119 | + return res |
| 120 | +``` |
| 121 | +### Approach 3: Hashing (Alternative Approach) |
| 122 | +##### Intuition: |
| 123 | +Another approach is to use a hash set to store the values we've already seen. For each pair of numbers, calculate the third number needed to make the sum zero and check if it exists in the hash set. |
| 124 | + |
| 125 | +Steps: |
| 126 | +1. Sort the array: Sorting helps in identifying and skipping duplicates. |
| 127 | +2. Iterate through the array: For each number, use a hash set to check if the complement of the sum exists. |
| 128 | +3. Check for duplicates: Ensure the result contains only unique triplets by checking for duplicates. |
| 129 | +##### Time Complexity: |
| 130 | +O(n²), as we iterate over the array and use hashing for each element. |
| 131 | +##### Space Complexity: |
| 132 | +O(n), for storing the hash set. |
| 133 | +##### Python Code: |
| 134 | +```python |
| 135 | +def threeSum(nums: List[int]) -> List[List[int]]: |
| 136 | + nums.sort() |
| 137 | + res = [] |
| 138 | + |
| 139 | + for i in range(len(nums)): |
| 140 | + if i > 0 and nums[i] == nums[i - 1]: |
| 141 | + continue |
| 142 | + |
| 143 | + seen = set() |
| 144 | + j = i + 1 |
| 145 | + while j < len(nums): |
| 146 | + complement = -nums[i] - nums[j] |
| 147 | + if complement in seen: |
| 148 | + res.append([nums[i], nums[j], complement]) |
| 149 | + while j + 1 < len(nums) and nums[j] == nums[j + 1]: |
| 150 | + j += 1 |
| 151 | + seen.add(nums[j]) |
| 152 | + j += 1 |
| 153 | + |
| 154 | + return res |
| 155 | +``` |
| 156 | +| Approach | Time Complexity | Space Complexity | |
| 157 | +|-----------------------------------|-----------------|------------------| |
| 158 | +| Brute Force | O(n²) | O(n) | |
| 159 | +| Sorting + Two Pointers (Optimal) | O(n²) | O(n) | |
| 160 | +| Hashing with Set | O(n²) | O(n) | |
| 161 | + |
| 162 | +The Sorting + Two Pointers approach is the most efficient and optimal solution. It leverages sorting and the two-pointer technique to solve the problem in O(n²) time and handles duplicate triplets efficiently. |
0 commit comments