给定一个包含 n 个整数的数组 nums
,判断 nums
中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4], 满足要求的三元组集合为: [ [-1, 0, 1], [-1, -1, 2] ]
这道题的思路和两数之和类似,只不过难度更胜一筹,还是同样的配方,没有思路的时候不妨尝试暴力解法
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
for (int i = 0; i < nums.length; i++) {
int first = -nums[i];
for (int j = 0; j < nums.length; j++) {
if (i != j) {
int third = first - nums[j];
for (int k = 0; k < nums.length; k++) {
if (k != i && k != j && nums[k] == third) {
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[j]);
list.add(third);
Collections.sort(list);
if (!res.contains(list)) {
res.add(list);
}
}
}
}
}
}
return res;
}
这样在我们自己的IDE里和数据量小的时候,可以得到正确解答,但是数据量一大便会timeout,因为这是一个O(n3)的算法,时间开销非常大。在有了这个方法之后,我们怎么来优化呢?从里面的第二层循环开始,发现和两数之和有着出奇的相似,但是两数之和是有序数组,这里是无序的,我们不妨先将数组排序,在采用二分查找O(nlogn + n2logn)或者对撞指针O(nlogn + n2)来解决问题,下面是对撞指针的解决方案,同时,这道题目里,一定注意注释给出的几个优化点,避免重复!!!
public List<List<Integer>> threeSum(int[] nums) {
List<List<Integer>> res = new ArrayList<>();
if (nums == null || nums.length < 3)
return res;
Arrays.sort(nums);
for (int i = 0; i < nums.length; i++) {
//如果num[i] > 0 再加两个比其大的数 不可能等于0 至此结束循环
if (nums[i] > 0) {
break;
}
//避免重复
if (i > 0 && nums[i] == nums[i - 1]) {
continue;
}
int target = -nums[i];
int left = i + 1;
int right = nums.length - 1;
//说明第一个数选小了 重新选第一个数 不必在循环
if (nums[i] + nums[right] + nums[right - 1] < 0) {
continue;
}
while (left < right) {
if (nums[left] + nums[right] == target) {
List<Integer> list = new ArrayList<>();
list.add(nums[i]);
list.add(nums[left]);
list.add(nums[right]);
res.add(list);
left++;
right--;
//避免重复
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
} else if (nums[left] + nums[right] < target) {
left++;
//避免重复
while (left < right && nums[left] == nums[left - 1]) {
left++;
}
} else {
right--;
//避免重复
while (left < right && nums[right] == nums[right + 1]) {
right--;
}
}
}
}
return res;
}
上面的代码实际上是一个O(n2)级别的算法,提交给Leetcode后便可以AC了,但是笔者将其改写成Python代码后,发现这样一个O(n2)的算法还是会timeout,如果有小伙伴可以优化下面的Python代码或者其他思路,欢迎一起交流分享
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
res = []
if len(nums) < 3:
return res
nums.sort()
for i in range(len(nums)):
if nums[i] > 0:
break
if i > 0 and nums[i] == nums[i - 1]:
continue
target = -nums[i]
left = i + 1
right = len(nums) - 1
if nums[i] + nums[right] + nums[right - 1] < 0:
continue
while left < right:
if nums[left] + nums[right] == target:
res.append([nums[i], nums[left], nums[right]])
left += 1
right -= 1
while left < right and nums[left] == nums[left - 1]:
left += 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
elif nums[left] + nums[right] < target:
left += 1
while left < right and nums[left] == nums[left - 1]:
left += 1
else:
right -= 1
while left < right and nums[right] == nums[right + 1]:
right -= 1
return res