2sum, 3sum 是典型的考查排序后数组的特性的一道题,一旦将数组排序,加和的话就通过 夹逼法 实现就可以。。。
如果数组内有重复数据出现,那么要考虑,是否可以过滤掉一些重复元素,在夹逼过程中,可以滑过一整段相同的数字,而不是每跳到一个数字就计算考察一下。。
public class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList<>(); // !!! my knowledge for container is so short
if(nums==null || nums.length<3) return list;
Arrays.sort(nums);
for(int i=0; i<nums.length-2; i++){
while((i>0) && nums[i]==nums[i-1]&& i<nums.length-3) i++;
if(nums.length>3 && i==(nums.length-3) && nums[nums.length-4]==nums[nums.length-3] ) return list;
// NB!!!!!! And the conditions in if() while() matters in ORDER!! safe to put the length first
// this is a FRAGMENT code, added because of an error
//while((i>0) && nums[i]==nums[i-1]&& i<nums.length-3) i++; // i<nums.length-3!! not i<nums.length-2!!
// since I hecked the previous i in last loop, so in order to avoid duplication, I should skip the same nums[] here
//while(nums[i]==nums[i+1]) i++; // get the last element of a sequence of same element, but this is wrong, since the starting point cannot be ignored, they can be a element!!! the only time you can ignore is when you find the match.
int l=i+1;
int r=nums.length-1;
while(l<r){
//while(nums[l]==nums[l+1] && (l+1)<r) l++;
//while(nums[r-1]==nums[r] && l<(r-1)) r--;
int sum=nums[l]+nums[r];
if(sum==-nums[i]){
List<Integer> item = new ArrayList<>();
item.add(nums[i]);
item.add(nums[l]);
item.add(nums[r]);
list.add(item);
while(nums[l]==nums[l+1] && (l+1)<r) l++; // 1: update either l or r is okay, updating both could be a little faster. 2: (l+1)<r !!! since you are in a while loop, you can constantly comparing: nums[l]==nums[l+1]. !!! So keep index with legal bounds
l++; // after while loop, l is at the position of last element of sequence of same elem, so have to ++
//while(nums[l]==nums[l+1] ) l++;
//while(nums[r-1]==nums[r] ) r--;
// or r--; does not matter, but have to updata either l or r
}
else if(sum<-nums[i]) l++;
else if(sum>-nums[i]) r--;
}
}
return list;
}
}
我今天写了第二遍,思路比上面的要清晰流畅一些。。。但是要注意的重点是:无论是i还是left,right,都需要先进位,然后再通过while()来判断是否跨过重复元素。。。我在ppt中有所标记,代码如下:
List<List<Integer>> list= new ArrayList<>();
int len= nums.length;
if(len<3) return list;
Arrays.sort(nums);
for(int i=0; i<len-2; i++){
while(i>0 && i<len-2 && nums[i]==nums[i-1]) i++; // 这里和下面的left,right的处理一模一样,只是说i在每次进for loop都+1了,所以不需要写,想想for就是while的一种简略写法,只不是把i++综合在一起写进来了。。。所以这里只需要直接通过while来判断。。。弄清楚这个了之后的固定i的三种变化情况,==, >, < 都是做同样处理。。。
int left=i+1;
int right=len-1;
while(left<right){
int sum=nums[i]+nums[left]+nums[right];
if(sum==0){
list.add(new ArrayList<Integer>(Arrays.asList(nums[i],nums[left],nums[right])));
/* List<Integer> match= new ArrayList<>();
match.add(nums[i]);
match.add(nums[left]);
match.add(nums[right]);
list.add(match);*/
//break; // 这是不能break的,在找到一个match之后,还会有其他match比如, [1,2,...,8,9] 1+9 和 2+8 都是match
left++;
while(left<right && nums[left]==nums[left-1]) left++;
right--;
while(left<right && nums[right]==nums[right+1]) right--;
//while(right>left && nums[right-1]==nums[right]) right--;
}
else if(sum<0){
//while(left<right && nums[left+1]==nums[left])
left++;
while(left<right && nums[left]==nums[left-1]) left++;
}
else{
//while(right>i && nums[right-1]==nums[right])
right--;
while(left<right && nums[right]==nums[right+1]) right--;
}
}
}
return list;