给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例:
给定数组 nums = [-1, 0, 1, 2, -1, -4],
满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]
解法一:排序+双指针
排序的目的是为了在自左向右遍历nums[first], nums[second], nums[third],能够方便的跳过重复元素。当nums[first]确定时,nums[second]与nums[third]可以通过双指针向两边相向移动的方式来找到target = -nums[first]。注意由于这题与两数之和那题不一样,会存在多组解,不唯一,因此second需要遍历全部first+1 ~ n - 1,third从n-1位置向左移动。
class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:n = len(nums)nums.sort()ans = list()# 枚举 afor first in range(n):# 需要和上一次枚举的数不相同if first > 0 and nums[first] == nums[first - 1]:continue# c 对应的指针初始指向数组的最右端third = n - 1target = -nums[first]# 枚举 bfor second in range(first + 1, n):# 需要和上一次枚举的数不相同if second > first + 1 and nums[second] == nums[second - 1]:continue# 需要保证 b 的指针在 c 的指针的左侧while second < third and nums[second] + nums[third] > target:third -= 1# 如果指针重合,随着 b 后续的增加# 就不会有满足 a+b+c=0 并且 b<c 的 c 了,可以退出循环if second == third:breakif nums[second] + nums[third] == target:ans.append([nums[first], nums[second], nums[third]])return ans
另一种书写方式。其中从11行开始与TwoSum一样,是两端双指针。区别在于TwoSum在遇到target后直接就退出了,二ThreeSum中需要继续向后移动j。也正因为解不唯一,因此移动j和k都需要用while来跳过移动方向上的重复元素。
class Solution:def threeSum(self, nums: List[int]) -> List[List[int]]:n = len(nums)nums.sort()ans = []for i in range(n):if i > 0 and nums[i] == nums[i - 1]:continuej, k, target = i + 1, n - 1, -nums[i]while j < k:if nums[j] + nums[k] == target:ans.append([nums[i], nums[j], nums[k]])# 跳过重复nums[j]后尝试下一个while j < k and nums[j + 1] == nums[j]:j += 1j += 1elif nums[j] + nums[k] > target:# 跳过重复nums[j]后尝试下一个while j < k and nums[k - 1] == nums[k]:k -= 1k -= 1else:# 跳过重复nums[k]后尝试下一个while j < k and nums[j + 1] == nums[j]:j += 1j += 1return ans
