## 15. 3Sum https://leetcode.com/problems/3sum/description/ <br> :::spoiler **Optimal Space & Time Complexity** ``` - Time complexity: O(n^2) - Space complexity: O(1) ``` ::: <br> <hr/> ## Solutions :::spoiler 東 ```javascript= ``` ::: <br> :::spoiler Hao ```javascript= function threeSum(nums) { const results = []; if (nums.length < 3) return results; // having the numbers in ascending order will make this problem much easier. // also, knowing the overall problem will take at least O(N^2) time, we can // afford the O(NlogN) sort operation nums = nums.sort((a, b) => a - b); let target = 0; for (let i = 0; i < nums.length - 2; i++) { if (nums[i] > target) break; if (i > 0 && nums[i] === nums[i - 1]) continue; let j = i + 1; let k = nums.length - 1; while (j < k) { let sum = nums[i] + nums[j] + nums[k]; if (sum === target) { results.push([nums[i], nums[j], nums[k]]); while (nums[j] === nums[j + 1]) j++; while (nums[k] === nums[k - 1]) k--; j++; k--; } else if (sum < target) j++; else k--; } } return results; } ``` ::: <br> :::spoiler YC ```javascript= /** * @param {number[]} nums * @return {number[][]} */ var threeSum = function(nums) { if(nums.length < 3) return []; //early return nums.sort((a,b) => a-b); const ans = [] for(let i = 0; i < nums.length; i++){ let left = i + 1; let right = nums.length - 1; if(nums[i] > 0) return ans; //early return because the array is sorted if(nums[i] === nums[i-1]) continue; while(left < right){ const sum = nums[i] + nums[left] + nums[right]; if(sum < 0) left++; else if(sum > 0) right--; else{ ans.push([nums[i], nums[left], nums[right]]); left++; right--; while(nums[left] === nums[left-1]) left++; while(nums[right] === nums[right+1]) right--; } } } return ans; }; ``` ::: <br> :::spoiler SOL ```javascript= ``` ::: --- ## Supplement / Discussion ### 東 ### Hao [JavaScript with lots of explanatory comments!](https://leetcode.com/problems/3sum/solutions/281302/javascript-with-lots-of-explanatory-comments/) "Three" pointers: employ indexs `i`, `j`, `k` to indicate the number. Iterate `i` with the following steps: 1. Fix `i` first, and so we have only two pointers `j`, `k` left. 2. Sum up `i`, `j`, `k` to see if reach `targetSum` while move `j` and `k` closer to each other 3. Add early break/continue during iterations - When `nums[i] > targetSum` - When `nums[i] === nums[i - 1]` - When `nums[j] === nums[j - 1]` - When `nums[k] === nums[k - 1]` ### YC 1. sort the array 2. set three variables: `i`, `left`, `right` - `i` is a for-loop variable - `left` and `right` serve as left-right pointer 4. remove duplicate triplets - `nums[i] === nums[i-1]` - `nums[left] === nums[left-1]` - `nums[right] === nums[right+1]` 5. (nice to have) early return - `nums.length < 3` - `nums[i] > 0`: because the array is sorted ### SOL #### 3 loops 👎🏻👎🏻👎🏻 > [-1,0,1,2,-1,-4] 1. Sort the input array in ascending order > [-4,-1,-1,0,1,2,] 2. Loop through each value in the array. 3. Calculate the target value needed for two additional elements to sum up to zero. 4. Iterate through each value in sub-array1 (remaining elements after current value). > -4, [-1,-1,0,1,2,] find a + b = 4 5. Calculate the value needed for the third element to complete the triplet sum. 6. Iterate through each value in sub-array2 (remaining elements after current value). 7. Search for the value in the sub-array2 that matches the calculated target value. 8. Add the triplet [a, b, c] to the result array if found. > -1, [,-1,0,1,2,] find 1 > .... ```javascript= var threeSum = function(nums) { const result = [] nums.sort((a,b)=>a-b) let preNum1; nums.forEach((a, index)=>{ if( preNum1 !== a ){ const target = 0 - a; const rest = nums.slice(index+1) let preNum2; rest.forEach((b, index)=>{ if(preNum2 !== b){ const target2 = target - b; const rest2 = rest.slice(index+1) const find = rest2.find((c)=> c === target2) if(find || find === 0){ result.push([a, b,find]) }; } preNum2 = b }) } preNum1 = a }); return result; }; ``` #### two poniters 👍👍👍 1. Sort the input array in ascending order. 2. Loop through the array, excluding the last two elements. 3. Skip duplicates. 4. Use a two-pointers approach (left-right pointers) that move toward each other. 5. Calculate the sum - If the sum === target: a. Add the triplet to the result array. b. ==Eliminate duplicate pointers by moving them.== c. Move both pointers. - If sum < target, move leftPointer. - If sum > target, move rightPointer. ```javascript= var threeSum = function(nums) { const result = [] const target = 0; nums.sort((a,b)=>a-b) for (let i = 0; i < nums.length - 2; i++) { if (i > 0 && nums[i] === nums[i - 1]) continue; let leftPointer = i+1; let rightPointer = nums.length -1; while( leftPointer < rightPointer ){ const sum = nums[i] + nums[leftPointer] + nums[rightPointer] if(sum === target){ result.push([nums[i], nums[leftPointer], nums[rightPointer] ]) while(nums[leftPointer + 1] === nums[leftPointer]) leftPointer++; while(nums[rightPointer - 1] === nums[rightPointer]) rightPointer--; leftPointer++; rightPointer--; } else if(sum<target){ leftPointer++; } else { rightPointer--; } } } return result; }; ``` --- #### Supplement (psuedo code from YC) ```js= /* * 3 variables: i, j, k for-loop 3 times: O(n^3) * j, k -> left, right: O(n^2) * remove duplicate triplet: i, left, right * */ var threeSum = function(nums) { // sort const ans = [] for(let i = 0, ...){ if(nums[i]>0) return ans; let left = i+1; let right = nums.length - 1; while(left < right){ const sum = ... if(sum < 0) left++ else if (sum > 0) right--; else{ ans.push([i,left,right]) left++; right--; while(left === left-1) left++; while(right === right+1) right--; } } } return ans; } ```