文章目录
给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
本题返回数组中的数字,所以打乱索引没关系
排序+双指针
先进行排序,便于确定双指针移动的方向
每次遍历时:
- 下标 i 表示此时遍历到的数组下标,**固定arr[**i],让其他两个数凑 它
- 下标L, R:进行双指针移动,L从i+1开始,R从数组最后开始
( i, j 要定义在遍历循环的里面,所以每次遍历进行重新定义 )
指针移动时进行去重:
- 要凑的当前数 arr[i],i 移动时要跳过相同的:比较(i > 0 && nums[i] == nums[i - 1] ; (只有第一个数nums[o]不考虑去重)
- 指针L, R的移动也要去重
中途指针发生移动,需要限制条件L<R,因为外面while的管不住这里
移动:while (L < R && nums[L] == nums[L + 1]) L++;
例如 :-1,-1,-1,0,1 ,经过去重左指针从第一个-1 到达第三个-1,与后面的数0不重复,但-1已经参加运算,所以还要++
再次移动 L++;
时间复杂度:O(n²)
遍历i一波:
L,R移动一波
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> list = new ArrayList();
int len = nums.length;
if (nums == null || len < 3)
return list;
Arrays.sort(nums);
// int i=1, j=nums.length-1; 考察i所在数,遍历时,右边两个数的位置是根据每次i位置设定的
for (int i = 0; i < len; i++) {
if (nums[i] > 0) break; //若当期数字nums[i]>0, 与右边两个数的和>0
//只有第一个数nums[o]不考虑去重
if (i > 0 && nums[i] == nums[i - 1]) continue;
int L = i + 1;
int R = len - 1;
while (L < R) {
int twoSum = nums[L] + nums[R];//右边两数之和
if (twoSum < -nums[i]) {
L++;
} else if (twoSum > -nums[i]) {
R--;
} else {
// list.add(new List[nums[k],nums[i],nums[j]]);
list.add(Arrays.asList(nums[i], nums[L], nums[R]));
//左右指针的移动也要去重
//中途指针发生移动,需要限制条件L<R,外面while的管不住这里
while (L < R && nums[L] == nums[L + 1]) L++;
// -1,-1,0,1 此时左指针从第一个-1 到达第二个-1,与后面的数不重复,但-1已经参加运算,所以还要++
L++;
while (L < R && nums[R] == nums[R - 1]) R--;
R--;
}
}
}
return list;
}
}
注:
忘记去重
[-1,-1,-1,0,1,1]:[-1,0,1],[-1,0,1]
Arrays.asList
list.add(Arrays.asList(nums[i], nums[L], nums[R]));
在java语言中,把数组转换成List集合,有个很方便的方法就是 List list = Arrays.asList(“a”,“b”,“c”);
但这样得到的List它的长度是不能改变的。当你向这个List添加或删除一个元素时(例如 list.add(“d”);)程序就会抛出异常(java.lang.UnsupportedOperationException)