题干
给你一个包含n个整数的数组nums,判断nums是否存在三个元素a,b,c,使得a+b+c = 0 ?请你找出所有和为0且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例
示例1
输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例2
输入:nums = []
输出:[]
示例3
输入:nums = [0]
输出:[]
题解
(一) 排序+双指针
时间复杂度:O(n^2) 空间复杂度:O(1)
思路及解法
边界情况:如果数组长度为null或者小于3,返回 []。
第一步:对数组进行排序;
第二步:遍历排序后的数组
1. 如果nums[i] > 0:因为已经排好序,所以后面不可能有三个数相加和等于0,直接返回结果
2. 对于重复元素:跳过,避免出现重复解
3. 令左指针 L =i+1,右指针R = n -1,当 L < R时,执行循环:
当nums[i] +nums[L] +nums[R] ==0, 执行循环,判断左界与右界是否和下一位置重复,去除重复解,并同时将L,R移到下一位置,寻找新的解
如果和大于0,说明nums[R]太大,R左移;
如果和小于0,说明nums[L]太小,L右移。
代码实现
public List<List<Integer>> threeSum(int[] nums){
List<List<Integer>> lists = new ArrayList<>();
//定义一个结果集
Arrays.sort(nums);
//排序
int len = nums.length;
for(int i = 0; i < len; i++){
if (nums[i] > 0) return lists;
//nums[i]为这三个数中的最小的数,如果nums[i]都大于零了,后面的更加不可能满足
if (i > 0 && nums[i] == nums[i-1]) continue;
//去重,当起始的值与上一个相同,那得到的答案一定相同
int curr = nums[i]; //第一个数的值
int L = i+1, R = len-1; //定义两个指针
while(L < R) {
int tmp = curr + nums[L] + nums[R];
//这三个数相加的值放到temp中
if(tmp == 0){ //得到满足条件的解
List<Integer> list = new ArrayList<>();
list.add(curr);
list.add(nums[L]);
list.add(nums[R]);
lists.add(list); //将此次结果放入结果集中
while(L < R && nums[L+1] == nums[L]) L++; //去重,当第二个数出现重复
while(L < R && nums[R-1] == nums[R]) R--; //去重,当第三个书出现重复
L++;
R--;
}else if (tmp < 0 ){ //和小于零,说明第二个数太小
L++;
}else { //和大于零,说明第三个书太大
R--;
}
}
}
return lists;
}
归纳与总结
- Arrays.sort()方法
这种形式是对一个数组的所有元素进行排序,并且是按从小到大的顺序。 - 去重
if (i > 0 && nums[i] == nums[i-1])
continue; //这个continue还蛮难想的 - 合理减少工程量:当nums[i] > 0,后面的元素都不用比较了。这个点做题的时候没想到
- 其实就是一个降维过程,从三维降到二维,固定一个值,用双指针,一个在头一个在尾,往中间靠拢,找答案
- ArrayList是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制
add()方法,可以将元素添加到ArrayList中
get()方法,可以访问ArrayList中的元素
set()方法,可以修改ArrayList中的元素(第一个为索引位置,第二个为要修改的值)
remove()方法,可以删除ArrayList中的元素(括号里是索引)
size()方法,可以计算ArrayList中的元素数量 - 要格外注意数组越界情况,给nums[i]去重的时候还要并上条件i>0;tmp的定义要放在while(L>R)循环中,不然可能报错