「这是我参与2022首次更文挑战的第6天,活动详情查看:2022首次更文挑战」。
题目:一个长度为n的数组nums,判断数组中是否存在三个数a,b,c,使得a+b+c=0,若存在,列出所有满足此条件的三元组,并且保证三元组不重复。
解题思路
此题的简单想法是首先判断数组的长度,如果长度小于3则直接返回空列表,大于3之后可以通过三层for循环遍历所有元素,如果存在等于0的情况则将元素add到LinkList中,之后返回一个结果即可,但此方式存在一个问题,如果数组元素全部是0且长度大于3,则会出现重复的情况。此时可以通过HashSet来解决,但还存在一种情况,例如以下程序:
public static List<List<Integer>> threeSum(int[] nums) {
if(nums.length<3){
return new LinkedList<>();
}
List<List<Integer>> Result = new LinkedList<>();
for(int i=0;i<nums.length;i++){
for(int j=i+1;j<nums.length;j++){
for(int m=j+1;m<nums.length;m++){
if(nums[i]+nums[j]+nums[m] == 0){
LinkedList<Integer> temp = new LinkedList<>();
temp.add(nums[i]);
temp.add(nums[j]);
temp.add(nums[m]);
Result.add(temp);
}
}
}
}
return Result;
}
如果输入的数组为:-1,0,1,2,-1,-4,则得到的结果为:[[-1, 0, 1], [-1, 2, -1], [0, 1, -1]],其中由于[-1, 0, 1]和[0, 1, -1]顺序不同,HashSet无法进行去重,需要使用更复杂的方式进行去重,并且时间和空间的耗费也很大,因此本题的难点就在于如何保证最后的结果不存在重复。
若想保证最后的结果不重复,首先关键就是将数组进行排序,排序后检查当前循环的元素,如果当前元素大于0则直接返回结果,因为就不存在其它可能为0的情况,每次数组元素后移的时候都必须检查当前元素和之前的元素是否相等,如果相等则结束本次循环开启下一次循环,若要满足三个数字相加之和为0,则需要通过双指针来移动三个数字,其中设置左指针为当前元素的下一个元素,而右指针指向最后一个元素,之后判断三个元素之和,如果元素之和大于0则代表右指针数字过大,则右指针左移,如果元素之和小于0则代表左边指针数值较小,则左指针加大,增大数值,如果元素之和为0则创建list收集结果,然后移动左右指针,判断左右指针移动后的值和之前的值是否相等,如果相等则继续移动,直到不相等。代码如下:
if(nums.length < 3){
return new ArrayList<List<Integer>>();
}
Arrays.sort(nums);
System.out.println(Arrays.toString(nums));
List<List<Integer>> result = new ArrayList<>();
for(int i=0;i<nums.length;i++){
if(nums[i]>0){
return result;
}
if(i>0&&nums[i] == nums[i-1]) continue;
int L = i+1;
int R = nums.length-1;
while(L<R){
int sum =nums[i] + nums[L] + nums[R];
if(sum== 0){
List<Integer> temp = new ArrayList<>();
temp.add(nums[i]);
temp.add(nums[L]);
temp.add(nums[R]);
result.add(temp);
L++;
R--;
while(L<R&&nums[L] == nums[L-1]){
L++;
}
while(L<R&&nums[R] == nums[R+1]){
R--;
}
}
if(sum > 0){
R--;
}
if(sum < 0){
L++;
}
}
}
return result;
}
时间复杂度分析
排序为快速排序,时间复杂度为,循环时间复杂度为,循环内双指针移动时间复杂度为,因此循环内时间复杂度为,空间复杂度为。