首先用直觉,直觉可以变成两数之和的问题,因为nums[i]+nums[j]+nums[k]==0,其实也就是-nums[i]=nums[j]+nums[k],想到两数之和是不够的,关键在于想到要把nums排序。排序可以同时解决两件事,一是避免重复的三元组,二是求两数之和的时候既然是从小到大排列的那么就不用双重循环了可以双指针。
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> res = new ArrayList<>(); Arrays.sort(nums); for(int i=0;i<nums.length-1;i++){ //避免重复的三元组措施1 //这一轮不要了,比如nums={-8,-8,-8,2,2,3,5,5,6},那么遍历完nums[0]=-8以后,nums[1]和nums[2]都跳过,直接去nums[3]=2 if(i>0 && nums[i]==nums[i-1]) continue; twoSum(nums,-nums[i],i+1,res); } return res; } /** []nums: 题目传来的原数组 sum:两数目标和 start:最左起点 res: */ public void twoSum(int []nums, int sum, int start,List<List<Integer>> res){ int small = start; int big = nums.length-1; //跟快排一样,small==big的时候退出循环 while(small<big){ if(nums[small]+nums[big]<sum){ small++; while(small<big && nums[small]==nums[small-1]){ small++; } //small跳过所有重复值,此时指向新值。避免重复的三元组措施2 }else if(nums[small]+nums[big]>sum){ big--; while(small<big && nums[big]==nums[big+1]){ big--; } //big跳过所有重复值,此时big指向新值 }else if(nums[small]+nums[big]==sum){。避免重复的三元组措施2 List<Integer> temp = new ArrayList<>(); temp.add(nums[small]); temp.add(nums[big]); temp.add(nums[start-1]); res.add(temp); //这里让small去走也行,让big去走也行,我用的small small++; while(small<big && nums[small]==nums[small-1]){ small++; } //small跳过所有重复值,此时指向新值。避免重复的三元组措施2 } } } }时间复杂度:排序算法时间复杂度O(nlogn),遍历过程中,外层循环把nums从左到右遍历一遍,内存循环双指针也是线性遍历,所以时间复杂度O(n^2)。整个算法的时间复杂度取O(nlogn)和O(n^2) 的最大值,所以是O(n^2)。
二、暴力解法
这个题小白很容易想到暴力解法,我们来看看暴力解法。
三重循环,麻烦在于对于三元组不重复的判断。一开始是下面的写法,很垃,直接不要看。
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> res = new ArrayList<>(); //把所有可能的两数之和都存进去 for(int i=0;i<nums.length-2;i++){ for(int j=i+1; j<nums.length-1;j++){ for(int k=j+1;k<nums.length;k++){ if(nums[i]+nums[j]+nums[k]==0){ //判断和已有的结果不重复才加入 if(!ifExsits(res,nums,i,j,k)){ List<Integer> temp=new ArrayList<>(); temp.add(nums[i]); temp.add(nums[j]); temp.add(nums[k]); res.add(temp); } } } } } return res; } /** 避免重复三元组 */ public true ifExsits(List<List<Integer>> res, int[] nums,int i,in j,int k){ //笛卡尔积的方式去判断 } }三元组不重复的正确判断方式应该是,先给nums排序,通过每一次进入循环判断跟上一次的数是否一样,一样的话跳出循环,这种方式来去重。
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> res = new ArrayList<>(); //排序,升序降序都可以-->为了三元组去重 Arrays.sort(nums); //把所有可能的两数之和都存进去 for(int i=0;i<nums.length-2;i++){ if(i>0 && nums[i]==nums[i-1]) continue; //三元组去重 for(int j=i+1; j<nums.length-1;j++){ if(j>i+1 && nums[j]==nums[j-1]) continue; //三元组去重 for(int k=j+1;k<nums.length;k++){ if(k>j+1 && nums[k]==nums[k-1]) continue; //三元组去重 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]); res.add(temp); } } } } return res; } }这种暴力解法会败在时间复杂度上: