1 题目
给你一个整数数组 nums ,判断是否存在三元组 nums\[i, numsj, numsk] 满足 i != j、i != k 且 j != k ,同时还满足 numsi + numsj + numsk == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
示例 1:
输入:nums = -1,0,1,2,-1,-4
输出:\[-1,-1,2,-1,0,1]
解释:
nums0 + nums1 + nums2 = (-1) + 0 + 1 = 0 。
nums1 + nums2 + nums4 = 0 + 1 + (-1) = 0 。
nums0 + nums3 + nums4 = (-1) + 2 + (-1) = 0 。
不同的三元组是 -1,0,1 和 -1,-1,2 。
注意,输出的顺序和三元组的顺序并不重要。
示例 2:
输入:nums = 0,1,1
输出:\[\]
解释:唯一可能的三元组和不为 0 。
示例 3:
输入:nums = 0,0,0
输出:\[0,0,0]
解释:唯一可能的三元组和为 0 。
2 分析
这道题乍一看最直观的解法是三重遍历,但时间复杂度是O(N^3),而且无法解决重复的问题。因此,进阶解法需要解决以下两个问题:
- 时间复杂度
- 重复元素
通过对数组排序,可以将相同的元素放到一起,且单调数组更容易做推断。接下来,只需要固定元素numsi,寻找j和k使得numsj+numsk=-numsi即可。让left和right指针分别指向数组左边的小元素和右边的大元素,基于单调性,如果numsleft+numsright<-numsi,只需要将left右移,就可以得到更大的和;如果如果numsleft+numsright>-numsi,只需要将right左移,就可以得到更小的和。这样只需要两层遍历。
另一个问题是怎么解决重复元素?只需要当前位置与前一个位置比较,如果相同则跳过。这里分三种情况,
- 第一种情况是固定位置numsi,只需要比较每个循环中numsi与前一个位置numsi-1是否相等,如果相等则不需要再做循环,直接continue即可。
- 第二种情况是left指针位置,当找到满足要求的三元组后,left指针会加一,right指针会减一,因此需要比较numsleft与前一个位置numsleft-1是否相等,如果相等则继续对left加一,直到left不比right小;
- 第三种情况是right指针位置,也是在找到满足要求的三元组之后,需要比较numsright与后一个位置numsright+1是否相等,如果相等则继续对right减一,直到right不比left大。
从时间复杂度上看,排序复杂度O(n log n) ,后续双层遍历复杂度O(n²)。根据大O表示法,O(n log n + n²) = O(n²)。因此时间复杂度为O(n²)。
3 代码
python
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
nums.sort()
n = len(nums)
res = []
for i in range(n-1):
if i>0 and nums[i]==nums[i-1]:
continue
target = -nums[i]
left, right = i+1, n-1
while left < right:
cur_sum = nums[left] + nums[right]
if cur_sum == 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 cur_sum > target:
right -= 1
else:
left += 1
return res