掘金团队号上线,助你 Offer 临门! 点击 查看详情
一、题目描述
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
二、思路分析
这道题主要考察了双指针的使用,且需要考虑到去重的问题,去重的常规思想是使用散列函数,针对数组,我们可以使用排序+jump的方式做到去重的实现,另外考虑边界问题,数组本身大小和要求的结果。
一开始的思路肯定是三个for循环,暴力求解,每层循环都要考虑到和上次的元素是否相等,如果相等,就跳过这次,这样的时间复杂度为O(N^3),后续考虑到优化问题,我们可以在第二层和第三层做处理。
- 先固定住第一层,因为数组已经从小到大排序故遍历第一层的时候如果元素的值大于0,那么后续的元素直接可以不用考虑了,另外,第一层的元素也必须满足不和上一次的元素重复
- 接下来两个指针,一个是从大于第一层的第一个元素开始,从前往后遍历,另一个是从最后一个元素往前遍历,且这两个指针不能相交,因为不能使用同一个元素两次,这样去枚举所有结果,因为这两个指针的遍历可以同步进行(左指针不动,右指针移动N-左指针个长度)在这一层的时间复杂度均摊下来为O(N),因此整个的时间复杂度为O(N^2),算上排序的时间复杂度O(nlogn),在渐进条件下,时间复杂度还是趋向于O(N^2)
三、AC代码
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
if(nums == null || nums.length <= 2){
return new ArrayList();
}
int n = nums.length;
Arrays.sort(nums);
List<List<Integer>> result = new ArrayList();
for(int i = 0 ;i<n;i++){
int l = i+1;
int r = n-1;
int target = -nums[i];
if(nums[i] > 0){
break;
}
if(i>0 && nums[i] == nums[i-1]){
continue;
}
while(l < r){
if(nums[l]+nums[r] == target){
//将寻找到的结果放入结果数组
List<Integer> temp = new ArrayList();
temp.add(nums[l]);
temp.add(nums[i]);
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--;
}
}else if(nums[l]+nums[r] < target){
//小于0,左指针增加,总数值增大
l++;
}else{
//大于0,右指针减小,总数值减小
r--;
}
}
}
return result;
}
}
四、总结
遇到需要枚举数组中的元素时候:如果需要枚举的第一个元素是递增的,第二个元素是递减的,我们就可以使用双指针的方式,枚举出所有需要的组合。