1.三数之和
-
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
-
注意:答案中不可以包含重复的三元组。
1.解题思路(迭代+快慢指针)
- 0.先把数组进行升序排列,Arrays.sort(nums);
- 1.建立3个指针,first、second、third,(以下使用first、second、third代替nums[first]、nums[second]、nums[third])可知,first <= second]<=third;
- 2.如果要实现first+second+third=0,则有first<=0,且second+third=-1×first;
- 3.题目要求:三元数组不可重复,所以first在每次for循环的时候需要枚举不同的值,second在每次for循环的时候需要枚举不同的值。
- 1.首先对于first来说,eg.本次for循环,first=-1,那么second和third,会遍历所有以-1为第1个元素的三元数组组合(-1,second、third);
- 2.然后对于second来说,eg:本次循环,first=-1,second=-1,那么指针third会从后向前移动,使得first + second + third = 0成立,在此过程中还要保证third指针严格在second指针的右侧,否则就continue;如果下次for循环,first=-1,second=0, 那么指针third还会从后向前移动,使得first + second + third = 0成立。
- 所以可知,在相同的second时,third也是相同的,所以second的每次枚举值需要不同,才要保证third的取值不同;
- 同理,每次first相同时,它所能匹配到的second+third组合也是相同的,所以first每次枚举值需要不同。
时间复杂度:O(n²)
空间复杂度:O(n)
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if (nums==null || nums.length<3){
return new ArrayList<>(0);
}
0.先进行数组排序
Arrays.sort(nums);
List<List<Integer>> res = new ArrayList<>();
1.对第一层指针first进行遍历
for (int first = 0; first < nums.length; first++) {
1.first<=0, 因为如果first>0,那么就会导致 third > second > first > 0 从而无法三数之和=0
if(nums[first]>0)
break;
2.每次first枚举值必须不同
if (first>0 && nums[first]==nums[first-1]) continue;
3.指针third从最后向前进行遍历,1次赋值即可,
因为随着sencod的后移,third只会比前1个second所要求的更小才对,所以没必要每次second遍历,third都从最后nums.length-1向前开始遍历
int third = nums.length-1;
for (int second = first+1; second < nums.length; second++) {
4.每次second指针枚举的元素必须都要不同
if(second>first+1 && nums[second] == nums[second-1])
continue;
5.在保证second在左 third在右的前提下,开始移动third
while (second<third && nums[first]+nums[second]+nums[third]>0)
--third;
6.如果指针重合,那就break,没必要再比下去了,后边的second都比当前second大, 所以根本找不到符合要求的third了
if (second==third)
break;
7.如果满足要求,就保存三元数组
if (nums[first]+nums[second]+nums[third]==0){
res.add(Arrays.asList(nums[first],nums[second],nums[third]));
}
}
}
return res;
}
}
2.解题思路(回溯算法)——但是超时
- 1.数据结构的哈希表set,来保证三元数组的不可重复;
- 2.要对数组进行Arrys.sort() 排序;
- 3.回溯算法找出数组的所有三元子集,然后在此过程中计算三元数组的和是否为0;
时间复杂度:指数级
class Solution {
Set<List<Integer>> set;
List<List<Integer>> res;
public List<List<Integer>> threeSum(int[] nums) {
if(nums==null&&nums.length<3){
return new ArrayList<>();
}
Arrays.sort(nums);
res = new ArrayList<>();
List<Integer> list = new ArrayList<>();
set = new HashSet<>();
backTrack(nums,0,list,0);
return res;
}
回溯算法
public void backTrack(int[] nums, int index, List<Integer> path,int sum){
1. 终止条件 数组长度为3 且 和为0 且 无重复
if (path.size()==3 && sum==0 && set.add(new ArrayList<>(path))){
res.add(new ArrayList<>(path));
return;
}
2.终止条件 数组长度为3 或者 index是num.length
if (path.size()==3 || index== nums.length)
return;
for (int i = index; i < nums.length; i++) {
1.做选择
sum += nums[i];
path.add(nums[i]);
当三元数组的第一个值大于0时,停止回溯,因为如果first>0,那么就会导致 third > second > first > 0 从而无法三数之和=0
if (path.get(0)>0)
return;
2.回溯
backTrack(nums, i+1, path, sum);
3. 撤销选择
sum -= nums[i];
path.remove((Integer) nums[i]);
}
}
}