|
| 1 | +# golang_three_sum |
| 2 | + |
| 3 | +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`. |
| 4 | + |
| 5 | +Notice that the solution set must not contain duplicate triplets. |
| 6 | + |
| 7 | +## Examples |
| 8 | + |
| 9 | +**Example 1:** |
| 10 | + |
| 11 | +``` |
| 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[4] = 0 + 1 + (-1) = 0. |
| 17 | +nums[0] + nums[3] + nums[4] = (-1) + 2 + (-1) = 0. |
| 18 | +The distinct triplets are [-1,0,1] and [-1,-1,2]. |
| 19 | +Notice that the order of the output and the order of the triplets does not matter. |
| 20 | +
|
| 21 | +``` |
| 22 | + |
| 23 | +**Example 2:** |
| 24 | + |
| 25 | +``` |
| 26 | +Input: nums = [0,1,1] |
| 27 | +Output: [] |
| 28 | +Explanation: The only possible triplet does not sum up to 0. |
| 29 | +
|
| 30 | +``` |
| 31 | + |
| 32 | +**Example 3:** |
| 33 | + |
| 34 | +``` |
| 35 | +Input: nums = [0,0,0] |
| 36 | +Output: [[0,0,0]] |
| 37 | +Explanation: The only possible triplet sums up to 0. |
| 38 | +
|
| 39 | +``` |
| 40 | + |
| 41 | +**Constraints:** |
| 42 | + |
| 43 | +- `3 <= nums.length <= 3000` |
| 44 | +- $`-10^5$ <= nums[i] <= $10^5$` |
| 45 | + |
| 46 | +## 解析 |
| 47 | + |
| 48 | +給定一個整數陣列 nums |
| 49 | + |
| 50 | +要求寫一個演算法找在所有在 nums 中3個和= 0 數字的所有不重複複組合 |
| 51 | + |
| 52 | +已知目標是找到 i, j , k , i < j < k , 使得 nums[i] + nums[j] + nums[k] =0 |
| 53 | + |
| 54 | +可以注意到 當 nums[j] 確定時, nums[i] + nums[k] 只能 - nums[j] |
| 55 | + |
| 56 | +所以可透過 類似 two sum 的作法 |
| 57 | + |
| 58 | +另外為了可以使用 two pointer 來縮小範圍 |
| 59 | + |
| 60 | +所以將原本的nums 做 sort 來做由小到大的排列 |
| 61 | + |
| 62 | +初始化 start = 0, index = 1, end = len(nums) - 1, result = [] |
| 63 | + |
| 64 | +當針對所有可能的 index 做檢查(使用 two pointer) |
| 65 | + |
| 66 | + |
| 67 | + |
| 68 | +為了避免重複檢查 |
| 69 | + |
| 70 | +所以如果 nums[index] = nums[index-1] 時 且 index > 1 代表已經nums[index]找過了 |
| 71 | + |
| 72 | +這時需要把 start = index - 1 讓搜索範圍變小 |
| 73 | + |
| 74 | +而對於 start < index 且 end > index 時 |
| 75 | + |
| 76 | +為了避免重複檢查 |
| 77 | + |
| 78 | +當nums[start] = nums[start-1] 且 start > 0 代表此搜索範圍已經找過了 |
| 79 | + |
| 80 | +這時需要把 start += 1 重新搜索 |
| 81 | + |
| 82 | +當nums[end] = nums[end+1] 且 end < len(nums) - 1 代表此搜索範圍已經找過了 |
| 83 | + |
| 84 | +這時需要把 end -= 1 重新搜索 |
| 85 | + |
| 86 | +令 addNum = nums[start] + nums[index] + nums[end] |
| 87 | + |
| 88 | +當 addNum = 0 代表找到符合條件的組合 |
| 89 | + |
| 90 | +把 [nums[start], nums[index], nums[end]] 加入 result , 更新 start += 1, end -= 1 |
| 91 | + |
| 92 | +當 addNum > 0 因為 start 已經是最小值 所以只能把 end 往左移 |
| 93 | + |
| 94 | +更新 end -= 1 |
| 95 | + |
| 96 | +當 addNum < 0 因為 end 已經是最大值 所以只能把 start 往右移 |
| 97 | + |
| 98 | +更新 start += 1 |
| 99 | + |
| 100 | +## 程式碼 |
| 101 | +```go |
| 102 | +package sol |
| 103 | + |
| 104 | +import "sort" |
| 105 | + |
| 106 | +func threeSum(nums []int) [][]int { |
| 107 | + if len(nums) < 3 { |
| 108 | + return [][]int{} |
| 109 | + } |
| 110 | + result := [][]int{} |
| 111 | + sort.Ints(nums) |
| 112 | + n := len(nums) |
| 113 | + // fixed index pivot |
| 114 | + for pivot := 1; pivot < n-1; pivot++ { |
| 115 | + start, end := 0, n-1 |
| 116 | + // do two sum for fixed index pivot |
| 117 | + if pivot > 1 && nums[pivot] == nums[pivot-1] { |
| 118 | + start = pivot - 1 |
| 119 | + } |
| 120 | + for start < pivot && pivot < end { |
| 121 | + if start > 0 && nums[start] == nums[start-1] { // update search range |
| 122 | + start++ |
| 123 | + continue |
| 124 | + } |
| 125 | + if end < n-1 && nums[end] == nums[end+1] { // update search range |
| 126 | + end-- |
| 127 | + continue |
| 128 | + } |
| 129 | + sum := nums[start] + nums[pivot] + nums[end] |
| 130 | + if sum == 0 { |
| 131 | + result = append(result, []int{nums[start], nums[pivot], nums[end]}) |
| 132 | + start++ |
| 133 | + end-- |
| 134 | + } else if sum > 0 { |
| 135 | + end-- |
| 136 | + } else { |
| 137 | + start++ |
| 138 | + } |
| 139 | + } |
| 140 | + } |
| 141 | + return result |
| 142 | +} |
| 143 | + |
| 144 | +``` |
| 145 | +## 困難點 |
| 146 | + |
| 147 | +1. 需要想出避免重複的方法 |
| 148 | +2. 需要想出對邊界值移動的條件 |
| 149 | + |
| 150 | +## Solve Point |
| 151 | + |
| 152 | +- [x] 初始化 i = 1 , result = [] |
| 153 | +- [x] 針對每個 i = 1.. len(nums)-1 做以下檢查 |
| 154 | +- [x] 初始化 start = 0, end = len(nums) - 1 |
| 155 | +- [x] 當 i > 1 且 num[i] == nums[i-1] 代表此數值已經搜尋過, 則更新 start = i - 1 |
| 156 | +- [x] 當 i > start 且 i < end 做以下檢查 |
| 157 | +- [x] 如果 start > 0 且 nums[start-1] = nums[start] 代表此範圍已經搜尋過,更新 start += 1 重新搜尋 |
| 158 | +- [x] 如果 end < len(nums) - 1 且 nums[end+1] = nums[end] 代表此範圍已經搜尋過,更新 end-= 1 重新搜尋 |
| 159 | +- [x] 令 addNum = nums[start] + nums[i] + nums[end] |
| 160 | +- [x] 當 addNum == 0 時 , 新增 [nums[start], nums[i], nums[end]], 更新 start+=1, end -= 1, 繼續搜尋 |
| 161 | +- [x] 當 addNum > 0 時 , 因為 start 已經是最小值所以只能把 end 左移, 更新 end -= 1, 繼續搜尋 |
| 162 | +- [x] 當 addNum < 0 時 , 因為 end 已經是最大值所以只能把 start 右移, 更新 start += 1, 繼續搜尋 |
| 163 | +- [x] 回傳 result |
0 commit comments