三数之和--双指针
要求找到所有「不重复」且和为 0 的三元组
排序 + 双指针(双指针只在有序时可以用)
有序的另一个作用是辅助去重,比如a,b,c//b,c,a//c,a,b对本题来说是一样的,有序后就只有a,b,c
同时,对于每一重循环而言,相邻两次枚举的元素不能相同,否则也会造成重复,因此:
nums.sort() for first = 0 .. n-1 // 只有和上一次枚举的元素不相同,我们才会进行枚举 if first == 0 or nums[first] != nums[first-1] then for second = first+1 .. n-1 if second == first+1 or nums[second] != nums[second-1] then for third = second+1 .. n-1 if third == second+1 or nums[third] != nums[third-1] then // 判断是否有 a+b+c==0 check(first, second, third)
接下来减少时间复杂度:
固定了前两重循环枚举到的元素 a 和 b,那么只有唯一的 c满足 a+b+c=0,可以从小到大枚举 b,同时从大到小枚举 c,即第二重循环和第三重循环实际上是并列的关系,时间复杂度从n^2降低到了n,因为每次b往右挪,c会接着上次的往左挪,而不是从最右边开始从头挪(b变大,c只会变小),等挪到相等就已遍历完,两个指针总计挪了n个位置
nums.sort() for first = 0 .. n-1 if first == 0 or nums[first] != nums[first-1] then // 第三重循环对应的指针 third = n-1 for second = first+1 .. n-1 if second == first+1 or nums[second] != nums[second-1] then // 向左移动指针,直到 a+b+c 不大于 0 while nums[first]+nums[second]+nums[third] > 0 third = third-1 // 判断是否有 a+b+c==0 check(first, second, third)
下面附上根据这个思路写的源代码:
class Solution { public List<List<Integer>> threeSum(int[] nums) { List<List<Integer>> al = new ArrayList<List<Integer>>(); //HashSet<Integer> h =new HashSet<Integer>(); //不能用Hash,因为数组中可能有下标不同但数值相同的,不能简单去重 Arrays.sort(nums); for(int i = 0;i<nums.length-2;i++){ if(i==0||nums[i]!=nums[i-1]){ int r = nums.length-1; for(int j=i+1;j<nums.length-1;j++){ if(j==i+1||nums[j]!=nums[j-1]){ while(nums[i]+nums[j]+nums[r]>0&&j<r){//保证右指针不会超过左指针 r--; } if(nums[i]+nums[j]+nums[r]==0&&j!=r){//避免左右指针重合的情况 List<Integer> t = new ArrayList<Integer>(); t.add(nums[i]); t.add(nums[j]); t.add(nums[r]); al.add(t); } } } } } return al; } }