給定一個包含 n 個整數的數組 nums,判斷 nums 中是否存在三個元素 a,b,c ,使得 a + b + c = 0 ?找出所有滿足條件且不重複的三元組。
注意:答案中不可以包含重複的三元組。
示例:給定數組 nums = [-1, 0, 1, 2, -1, -4],
滿足要求的三元組集合爲:
[ [-1, 0, 1], [-1, -1, 2] ]
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
}
}
這道題目很有意思,這裏提供兩種方法:
首先第一種是蠻力法,也是我最先想到的,嗯
這個方法的優點是思維簡單,缺點是時間複雜度高,當輸入數據較多時,難以獲得有效的滿意度。
蠻力法的思路是三次循環計算每三個組合,然後進行計算滿足的三個數,和已在表中的進行對比進行去重,依次做下去。
代碼如下:
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> a=new ArrayList<>();
int len=nums.length;
for(int i=0;i<len-2;i++){
for(int j=i+1;j<len-1;j++)
for(int k=j+1;k<len;k++){
if(nums[i]+nums[j]+nums[k]==0){
List<Integer> temp=new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[k]);
temp.sort(null);//有序的才能進行比較
if(!a.contains(temp)){
a.add(temp);
}
}
}
}
return a;
}
第二種是雙指針法,我看了下別人的題解,用自己的語言給大家整理下
我們爲了減輕複雜度,要對數組進行排序
然後呢我們對數組進行遍歷,從小到大依次爲最小值,當最小值都大於0時,必三數之和大於0,所以我們某種意義上就排除了一半的時間複雜度。
接下來就是循環過程中,右指針是最大值開始,左指針爲最小值的前一個開始。當右指針小於等於左指針時我們要進行下一輪循環,同時在本輪循環中,當三數和大於0時,我們將右指針向左移,也就是三數之和變小
當三數之和小於0時,我們將左指針向右移,三數之和也就變大了。
我們減輕複雜度的方法,比蠻力法少了列表去重,因爲我們將數組排好序後,重複的元素通過指針就自動去重了。
代碼如下:
public static List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> a=new ArrayList<>();
int len=nums.length;
Arrays.sort(nums);//數組排序
if(len<3||nums==null)
return a;//極限條件,數組爲空或長度小於3時
else if(nums[0]<=0&&nums[len-1]>=0){//當最小都大於0,或是最大都小於0,也就無三數之和==0的可能性了。
for(int i=0;i<len;i++){//開始循環
if(nums[i]>0) break;//最小值大於0時跳出循環
if(i>0&&nums[i]==nums[i-1]){
continue;//去除最小值的重複元素
}//確定左右指針位置
int Left=i+1;
int Right=len-1;
while(Left<Right){
int sum=nums[i]+nums[Left]+nums[Right];
if(sum==0){
a.add(Arrays.asList(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--;
}
else if(sum<0)
Left++;//和小於0左指針右移,和變大
else if(sum>0)
Right--;//和大於0 右指針左移,和變小
}
}
}
return a;
}
最後宣傳下我個人的微信公衆號,微信搜索:可及的小屋,有志向整副業,娛樂的程序員們,歡迎您的到來。謝謝。
100G程序員資料,自取哦!!