No.15, 3Sum
No.16, 3Sum Closest
前一个题主要是算3个数字加和等于target的三个数字,可能有多组,输出全部可能性,数字可能重复。
后一个题主要是算3个数字加和离target最近的值,结果存在且唯一。
两个题目的解题思路基本一致,首先最暴力的方法当然是O(n3)三重循环,这里我们不做讨论。
我们考虑如何让复杂度降到O(n2)。首先排序的复杂度不会大于该值,我们可以先排序。
基本思路为,首先固定一个数,可以得到另外两个数应该的和target,在剩余数组中,分别从头和尾开始遍历(头数字最小,尾数字最大),下标为i和j,如果两个加和大于target,说明大的数太大,j--,如果两个加和小于target,说明小的数太小,i++。
在前一题中,需要注意数字可能会重复,而结果要求不重复,那么应该在程序中判断是否已经计算过,计算过的直接跳过。
第二题中,如果跳过已经计算过的,可以再优化一些时间。最终submit版本我不小心去掉了。(一开始在里面绕了很久,不知道为什么这种算法不会漏掉正确答案,其实这里可以采用反证法证明,事实上最靠近target的都会尝试到,无论是比target大一点点还是小一点点。)
public class Solution { public List<List<Integer>> threeSum(int[] nums) { List result=new ArrayList<List<Integer>>(); Arrays.sort(nums); for(int i=0;i<nums.length;i++){ if(i>0&&nums[i]==nums[i-1]){ continue; } int target=0-nums[i]; int m=i+1,n=nums.length-1; while(m<n){ if(m>i+1&&nums[m]==nums[m-1]){ m++; continue; } if(n<nums.length-1&&nums[n]==nums[n+1]){ n--; continue; } if(nums[m]+nums[n]>target){ n--; } else if(nums[m]+nums[n]<target){ m++; } else{ List r=new ArrayList<Integer>(); r.add(nums[i]); r.add(nums[m]); r.add(nums[n]); result.add(r); //System.out.println(nums[i]+" "+nums[m]+" "+nums[n]); m++; } } } return result; } }
public class Solution { public int threeSumClosest(int[] nums, int target) { Arrays.sort(nums); int result=0; int closest=Integer.MAX_VALUE; for(int i=0;i<nums.length;i++){ int m=i+1,n=nums.length-1; while(m<n){ int t=target-(nums[m]+nums[n]+nums[i]); if(t<0){ if(closest>-t){ closest=-t; result=target-t; } n--; } else if(t>0){ if(closest>t){ closest=t; result=target-t; } m++; } else{ return target; } } } return result; } }