一、問題描述
Given an array nums
of n integers, are there elements a, b, c in nums
such that a + b + c = 0? Find all unique triplets in the array which gives the sum of zero.
Note:
The solution set must not contain duplicate triplets.
Example:
Given array nums = [-1, 0, 1, 2, -1, -4],
A solution set is:
[
[-1, 0, 1],
[-1, -1, 2]
]
二、解題思路
3 Sum問題可以轉化爲2 Sum問題(類似4 Sum問題可以轉化爲3 Sum)。這類問題最優解的時間複雜度爲O(n*(k-1))。即:2 Sum最優複雜度爲O(n),3 Sum最優爲O(n * n)。
對於2 Sum有兩種解題思路:
①先排序,然後用left和right分別指向開頭和結尾,之後計算和。同時根據情況移動即可。感覺這種方法像是快排。時間複雜度是:O(NlogN);
②就是利用unordered_map(底層爲哈希表),查找一個數爲線性時間,這樣的話一次遍歷即可找到,所以複雜度就降低爲:O(n)。
因此,對於3 Sum也有兩種方法。
基於①:首先要進行一次排序,然後就轉爲2 Sum問題,複雜度:O(N*NlogN)
基於②:先進行一次排序,然後也是2 Sum問題,複雜度:O(N * N).
三、代碼實現(C++)
基於①:首先要進行一次排序,然後就轉爲2 Sum問題,複雜度:O(N*NlogN)
#include <vector>
#include <algorithm>
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
if(nums.size() < 3){
return result;
}
sort(nums.begin(),nums.end());
//因爲題目中要求和爲0,而且此時已經排過序,因此第一個元素必須要<0
for(int i=0; i<nums.size() && nums[i]<=0; i++){
int target = -nums[i];
int left = i+1;
int right = nums.size()-1;
while(left < right){
int sum = nums[left] + nums[right];
if(sum < target){
left++;
} else if(sum > target){
right--;
} else{
result.push_back({nums[i],nums[left],nums[right]});
//防止把相同的第二個/第三個數放進去
while( left<right && (nums[left] == nums[left+1])) left++;
while( left<right && (nums[right] == nums[right-1])) right--;
//找到滿足的數後,下標要變化
left++;
right--;
}
}
//第一次寫時這裏用nums[i++] == nums[i],得出的結果不對。原因就是忽略了++特性
//這裏i++,後面由來了個i++,因此導致得不出正確的結果
while( (i+1)<nums.size() && nums[i+1] == nums[i] ) i++;
}
return result;
}
};
基於②:先進行一次排序,然後也是2 Sum問題,複雜度:O(N * N).
#include <unordered_map>
#include <algorithm>
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> result;
unordered_map<int, int> map;
if(nums.size() < 3){
return result;
}
sort(nums.begin(),nums.end());
for(int i=0; i<(nums.size()-1) && nums[i]<=0; i++){
int target = -nums[i];//從下面開始就是2 Sum的問題了
for(int j=(i+1); j<nums.size(); j++){
int other = target - nums[j];
if(map.find(other) != map.end()){
//加這個判斷是防止[-1,0,1],[-1,1,0]情況,因爲vector是有序的
if(nums[j]<=nums[map[other]]){
result.push_back({nums[i],nums[j],nums[map[other]]});
}
}else {
//去掉重複的
if(map.find(nums[j]) == map.end()) map[nums[j]] = j;
}
}
//防止第一個數據重複,即:[-1,-1,2],[-1,-1,2].因爲樣例可能是[-1,0,1,-1,-1,2]
while( (i+1)<nums.size() && nums[i+1] == nums[i] ) i++;
}
return result;
}
};
說明,對於這個方法。有個測試用例一直不過:
Input:[-1,0,1]
Output:[]
Expected:[[-1,0,1]]
我也一步一步的跟下去,發現是這樣的:初始當i=0,j=1時,此時map爲空,因此找不到1.所以,將0添加到map中(此時map爲<0,1>);當i=0,j=2時,本來可以用map中拿到0,但是我在代碼21行加了判斷:if(nums[j]<=nums[map[other]]),因此導致找不到。這也是個遺留問題吧。希望後面可以解決!!!