题目链接
题目解析
1> 选出的三元组里面的每个元素是不同位置的
2> 三个数相加是0
通过示例一我们可以知道
3> 我们三元组是不能重复的(不管位置,说明我们需要去重)
4> 三元组的顺序是不用管,内容不同就行(就可以进行排序了)
算法原理
解法一:
先排序+暴力枚举+利用set去重
时间复杂度: O(n^3)
解法二:
双指针+ 双指针
对于有序数组,我们要想到二分算法和双指针算法(可以降阶)
1> 排序
2> 先固定一个a
3> 在剩下的区间里面找俩个数之和为-a的数. 此时我们可以定义left和right来遍历剩下的区间(left从"头开始找,right从尾开始找)
处理细节问题:
1> 去重
我们注意到,我们已经排好序了,那么结果一样的数已经被放在一起了
比如下面的例子
当我们的0和4已经找到结果和为4的值了,因为排好了顺序,那么我们就可以跳过后面重复的0和4了.
同理i指向的元素也需要判断是否是重复元素
当使用完一次双指针算法之和,i也需要跳过重复元素
也就是找到一种结果之和,left和right指针要跳过重复的元素
去重: 1. 注意left和right的去重. 2. 注意i的去重
这里要注意,我们去重移动i,left,right的时候也要考虑越界的问题
2> 不漏: 我们找到一个之后要继续寻找,不要停
3> 当我们的a都为正数了就不需要去后面继续找下去,因为后面的数不可能有负数
编写代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
//创建二维数组,每次i遍历完就加入
List<List<Integer>> list = new ArrayList<>();
//1. 先进行排序
Arrays.sort(nums);
//先进行第一层循环
for(int i = 0;i < nums.length;){
//2.用a来记录i指向的数
int a = -nums[i];
//判断a是不是负数
if(a < 0){
break;
}
//设定left和right
int left = i+1;
int right = nums.length - 1;
//3. 利用双指针算法来找到俩个和为-a的的数
//第二层循环
while(left < right){
int sum = nums[left] + nums[right];
if(sum < a && left < right){
left++;
}else if(sum > a && left < right){
right--;
}else{
//加入一维数组
list.add(new ArrayList<Integer>(Arrays.asList(nums[i],nums[left],nums[right])));
//缩小区间继续寻找
left++;
right--;
//去重操作: left,right
while(nums[left] == nums[left-1] && left < right){
left++;
}
while(nums[right] == nums[right+1] && left < right){
right--;
}
}
}
//去重i
i++;
while(i < nums.length && nums[i] == nums[i-1]){
i++;
}
}
return list;
}
}
需要注意的地方
我们的i进行去重操作是不能写在定义的时候的,需要操作完,找到了那一个阶段的元组之后才可以去重.
Arrays.asList(....)这个函数可以直接就生成指定数的列表