題目:
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]]
一眼看這個題目感覺還行啊,於是無腦窮舉遍歷。
代碼如下:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<int>::iterator iter,iter2,iter3;
vector <vector<int> >::iterator iter4;
int i,a,b,c,k;
vector<vector<int>> res;
if(nums.size()<3)
return res;
sort(nums.begin(),nums.end());
k=0;
for(iter=nums.begin();iter!=(nums.end()-2);++iter)
{
for(iter2=(iter+1);iter2!=(nums.end()-1);++iter2)
{
for(iter3=(iter2+1);iter3!=nums.end();++iter3)
{
vector<int> res1;
if((*iter+*iter2+*iter3)==0)
{
res1.push_back(*iter);
res1.push_back(*iter2);
res1.push_back(*iter3);
res.push_back(res1);
k++;
}
}
}
}
//去重
sort(res.begin(),res.end());
for(iter4=res.begin();iter4!=res.end();)
{
if(*iter4==*(iter4+1))
{
res.erase(iter4);
}else{
iter4++;
}
}
return res;
}
};
想着數據量好像也不大啊,沒啥毛病啊,可能是我寫的太low了。(三個循環肯定超時)
於是只能想着用別的辦法了。
想象下一個數組中,找兩個數,他們的和爲0,那麼可以表示爲a+b=0,一般用兩個循環就可以解決了,(當然還有更加高效的解法,這裏就不引申了),那麼三個數呢?題目中三個數是這樣表達的a+b+c=0,那是不是可以變換下等式,a+b=-c,這樣一來我們可以先找到兩個數的組合,然後匹配第三個數,這樣一來就可以用兩個循環解決了,時間複雜度上就降了很多,從原來的,就變成了。那麼具體要怎麼實現能?我下面簡單說明下思路。(瞭解了三個數相加後可以考慮下k個數相加的情況)
首先當然是對這個數組進行排序處理,這樣有什麼好處呢?排序處理後我們就可以從數組當中首位開始取數,一個個往後找,可以避免很多重複的,對重複的數可以直接跳過,同時在找第三個數c的時候也可以按順序往前找,可以減少很多操作。
然後第一重循環可以先找數組中的第一個數nums[0]作爲a,然後nums[0+1]作爲b。那麼第二重循環,可以找第三個數了,這樣兩個循環就能解決問題了,但是第三個數從哪裏開始找呢?
看上面這張圖,既然a和b都是複數,並且這兩個數是整個數組中最小的,那麼它們倆相加的值是不是也是所有數中的兩個數相加和最小的呢?答案是肯定的,那麼後面c就要取一個最大的數來相加了,這樣結果纔可能等於0,數組已經排好序了,這樣我們就可以從數組中從後往前找了,最後一個數肯定是最大的,這就是前面爲什麼排序的原因之一了。
這裏分別用i,j,k,來控制a,b,c的下標移動。
那麼如上圖所示一開始可以取a=nums[i],b=nums[j] (j=i+1),c=nums[len-1] (len=nums.size())
然後第一重循環可以控制i的移動,第二重循環可以控制j和k的移動。
首先i取0的時候,j取1,k取len-1 (第一重循環)
(下面是第二重循環)
1.然後判斷a+b是否小於-c,如果a+b小於-c了,那麼這種情況c的下標k就不需要往前移了,再往前移c只會越來越小。所以這裏就直接b的下標往後移一個j++,如上圖這個時候j就是在2這個位置了,然後再繼續從最後找c。
2.再判斷a+b是否大於-c,如果a+b大於-c,那麼c的下標k就往前移找一個更小的,直到a+b=-c,如果找不到那就結束,繼續找下個b
3.最後一個判斷a+b是否等於-c,如果a+b等於-c,那麼結果就是需要找的這個-c,然後就把這三個數存到vector中去。
關鍵步驟就結束了,但是在第二個循環開始之前還是可以再做點優化的,比如第一個數=,這種情況可以直接跳過,因爲和前面那個數重複了,後面都是做重複的操作。還有當>0的時候,這裏就可以直接結束整個遍歷了,因爲一個排好序的數組,不可能三個大於0的整數相加等於0的,所以直接結束。
下面貼上代碼:
class Solution {
public:
vector<vector<int>> threeSum(vector<int>& nums) {
vector<vector<int>> res;
if(nums.size()<3)
return res;
sort(nums.begin(),nums.end());
for(int i=0,len=nums.size();i<len;i++)
{
if(nums[i]<=0)
if(i==0||nums[i]>nums[i-1]){
int j=i+1;
int k=len-1;
while(j<k)
{
vector<int> res1;
if(nums[i]+nums[j]+nums[k]<0){
j++;
}else if(nums[i]+nums[j]+nums[k]>0){
k--;
}else{
res1.push_back(nums[i]);
res1.push_back(nums[j]);
res1.push_back(nums[k]);
res.push_back(res1);
j++;
k--;
}
}
}
}
//去重
vector<vector<int>>::iterator iter;
sort(res.begin(),res.end());
for(iter=res.begin();iter!=res.end();)
{
if(*iter==*(iter+1))
{
res.erase(iter);
}else{
iter++;
}
}
return res;
}
};
瞭解了上面的三個數相加等於0的思路後,可以思考下四個數相加等於0的情況。
三個數相加等於0,我們可以看成兩個數相加的問題的擴展,也就是時間複雜度從 ,就變成了,降了一次,那麼四個數相加的問題是不是也可以考慮成三個數相加的問題呢?答案是肯定的。這樣一來,我們可以在第一個循環後面加一個循環就能解決四個數相加的問題了。那麼k個數呢?
k個數也可以模仿上面的思路把降爲,所以這種思路最多也就能優化降一次方,不知道還有沒有別的優化方法了,如果還有別的更好的辦法大家可以在下面評論中給出。
下面貼下4Sum的代碼,對應leetcode上的第十八題。
class Solution {
public:
vector<vector<int>> fourSum(vector<int>& nums, int target) {
vector<vector<int>> res;
if(nums.size()<4)
return res;
sort(nums.begin(),nums.end());
int len=nums.size();
for(int i=0;i<len;i++)
{
//if(nums[i]<=target)
if(i==0||nums[i]>nums[i-1]){
for(int m=i+1;m<len;m++)//這裏多加了一個循環
{
int j=m+1;
int k=len-1;
while(j<k)
{
vector<int> res1;
if(nums[i]+nums[m]+nums[j]+nums[k]<target){
j++;
}else if(nums[i]+nums[m]+nums[j]+nums[k]>target){
k--;
}else{
res1.push_back(nums[i]);
res1.push_back(nums[m]);
res1.push_back(nums[j]);
res1.push_back(nums[k]);
res.push_back(res1);
j++;
k--;
}
}
}
}
}
//去重
vector<vector<int>>::iterator iter;
sort(res.begin(),res.end());
for(iter=res.begin();iter!=res.end();)
{
if(*iter==*(iter+1))
{
res.erase(iter);
}else{
iter++;
}
}
return res;
}
};