约束范围:
解法:先排序再降维处理,将三数之和降成一个常数+两数之和。由约束条件知无需处理数组没有三个元素及null的情况。关键是去掉重复的数组。
1、将数组从小到大排序
Arrays.sort(nums);
2、降维处理
a、当常数>0时跳出循环,因为数组是从小到大排序,所以如果第一个数(也就是常数>0,后面的三数之和一定大于0,不符合题意要跳出循环)。
b、常数去重,使用 if(i>0 && nums[i]==nums[i-1]){continue;} 其中i>0是因为考虑到全为0的特殊数组。写成nums[i]==nums[i-1] 而不nums[i]==nums[i+1] 是因为写成nums[i]==nums[i+1] 就会使nums[i]和nums[i+1]这两个常数都无法继续处理,而且会导致缺少解,例如-4,-1,-1,0,1,2。它的解为[-1,-1,2]和[-1,0,-1],写成nums[i]==nums[i+1] continue 会从下标为2的数开始,会漏掉[-1,-1,2]这组解
c、假设nums[i]为常数,有两个指针:left和right,分别指向i的后一位和末尾,而不是指向0和末尾,这样做是为了去重。
d、left和right指针内容去重
while(right >0 && nums[right-1]==nums[right]) {right–;}
while(left<n-1 && nums[left]==nums[left+1]){ left++;}
完整代码:
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
int n=nums.length;
Arrays.sort(nums);
List<List<Integer>> list= new ArrayList<>();
for(int i=0;i<n;i++){
int left=i+1;int right=n-1;
if(nums[i]>0){break;}
if(i>0 && nums[i]==nums[i-1]){continue;}//考虑到全是0的数组因此i>0 且nums[i]==nums[i-1]
while(left<right){
int sum=nums[left]+nums[right]+nums[i];
if(sum>0){
right--;
}else if(sum==0){
List<Integer> listemp= new ArrayList<>();
listemp.add(nums[left]);
listemp.add(nums[i]);
listemp.add(nums[right]);
list.add(listemp);
while(right >0 && nums[right-1]==nums[right]) {right--;}
while(left<n-1 && nums[left]==nums[left+1]){ left++;}
left++;//这里同时使left和right变化是因为剩下为遍历的数组两端与它们各自的前一位,后一位都不同,只移动左或右都无法使三数的和为零,因此要同时向中间逼近。
right--;
}else{
left++;
}
}
}
return list;
}
}
今日感悟:我先是看完解题再做hot15 三数之和,由于只是按自己理解去重因此花了4天时间求解还没求出来,最后看了答案代码后才解出,最终的代码也和答案差不多。
二刷感悟:今日花了55分钟完成了此题,有许多欠缺考虑的地方。例如常数去重,该用if(i>0 && nums[i]==nums[i-1]){continue;} 而我用while(nums[i+1]==nums[i]){
i++;
}