1.题目
15. 三数之和
给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
2.自我思路及实现
三重循环
使用哈希表存储所有的值及角标,二重循环得到所有的两个数的和,判断哈希表中是否存在该和的负值
为了防止重复,哈希表中存的数,与二重循环中得到的两个数角标不同
问题:哈希表相同元素会覆盖
3.总结思路及实现
排序+双指针
不重复的本质
我们保证三个数 a ≤ b ≤ c,那么只有(a,b,c)被枚举到,(b,c,a)等不会
- 排序
相邻两次枚举的元素也不能相同:如果a在数组中出现了多次a1 = a2,那么枚举时(a1,b,c)和(a2, b , c)是重复的,b,c同理
- 枚举时跳过相同元素
优化:在确定a, b的情况下,c是唯一的,当b增加(向右移动)
,满足要求的c一定减小(向左移动),因此双指针可实现且减小时间复杂度
算法步骤
1.特判:数组为空或者长度小于3返回[ ]
2.排序:Arrays.sort();
3.遍历数组
- 当a = nums[i] > 0,因为数组已排序,bc不可能存在,此时跳出遍历
- 对于重复的a, 跳过,避免重复解
- 左指针为 i + 1, 右指针为n - 1
- 当左指针小于右指针时循环
-
- 和为0时,保存结果,左指针指向不重复的下一个元素,因为a+b+c中,abc都是唯一的,b改变那么c也要改变,右指针指向不重复的前一个元素,寻找新的解
-
- 和小于0,b+ c应该大一点,c无法增大,那么增大b,左指针移动至下一个不重复元素
-
- 和大于0, b+ c应该小一点,b无法减小,那么减小c,右指针移动至前一个不重复元素
时间:N2, 排序nlogn,循环n, 双指针最多移动n
空间:1
class Solution {
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new LinkedList<>();
if(nums == null || nums.length <= 2)
return res;
Arrays.sort(nums);
for(int i = 0; i < nums.length; i++)
{
if(nums[i] > 0)
break;
if(i > 0 && nums[i] == nums[i - 1])
continue;
int left = i + 1;
int right = nums.length - 1 ;
while(left < right)
{
int temp = nums[i] + nums[left] + nums[right];
if(temp == 0)
{
LinkedList<Integer> list = new LinkedList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
res.add(list);
//左右指针指向下一个不重复数
while(left < right && nums[left] == nums[left + 1])
left++;
while(right > left && nums[right] == nums[right - 1])
right--;
left++;
right--;
}
else if(temp < 0)
left++;
else
right--;
}
}
return res;
}
}
4.相关知识
不重复
- 保证三个数 a ≤ b ≤ c
- 枚举时跳过相同元素
需要枚举数组中的两个元素时,如果随着第一个元素的递增,第二个元素是递减的,那么就可以使用双指针的方法,将枚举的时间复杂度从 O(N^2)减少至 O(N)