题目
给你一个整数数组 nums ,判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i != j、i != k 且 j != k ,同时还满足 nums[i] + nums[j] + nums[k] == 0 。请你返回所有和为 0 且不重复的三元组。
注意:答案中不可以包含重复的三元组。
思路
基础解法:三层循环+对结果排序去重
思路
1、找到i,j,k位置上,≈=0的值
i、j、k分别以i从0...n, i<j<k的顺序循环取值,然后判断i,j,k位置上的值加起来是否为0,如果是,则视为结果
2、结果去重
对
[nums[i], nums[j], nums[k]]列表进行排序,后查询是否在结果中,如果不在,再保存到结果
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
# 解法1: O(N^3)
res = []
if len(nums) < 3:
return
for i in range(len(nums)):
for j in range(i+1, len(nums):
for k in range(j+1, len(nums)):
if nums[i] + nums[j] + nums[k] == 0:
# 通过排序来去重
if sorted([nums[i], nums[j], nums[k]])not in res:
res.append(sorted([nums[i], nums[j], nums[k]]))
return res
基础解法:三层循环+循环遍历时去重
不重复本质,三重循环等价于独立的每一层循环都不重复+两层之间有顺序:
- 第一层循环元素不重复
- 第二层循环元素不重复,且第二层循环元素不小于第一层循环元素
- 第三层循环元素不重复,且第三层的循环元素不小于第二层循环元素
第一层i1、i2....in有序,且本层相邻元素不重复;第二层j1、j2....jn有序,且本层相邻元素不重复;第三层k1、k2....kn有序,且本层相邻元素不重复
所以nums需要排序,排序后检查每层的相邻是否重复,如果重复指针,继续往右移,否则进入下一层
# 伪代码
# O(N^3)
nums.sort() # 排序
for first in 0...n-1: # 第一层
if nums[first] == nums[first-1]: # 相邻不允许重复
continue
for second in first+1...n-1: # 第二层
if nums[second] == nums[second-1]: # 相邻不允许重复
continue
for third in second+1...n-1: # 第三层
if nums[third] == nums[third-1]: # 相邻不允许重复
continue
check(first, second, third, num) # check是否满足条件
参考解法:排序+双指针
第二层和第三层其实一直在一个有序数组中,查找 nums[j] + nums[k] = -nums[i] 的操作,和 twoSum(nums, target)的题目要求一模一样,所以可以用双指针O(N)代替第二层+第三层循环的O(N^2)的代码。
# 伪代码
# O(N^3)
nums.sort() # 排序
res = []
for first in 0...n-1: # 第一层
if nums[first] == nums[first-1]: # 相邻不允许重复
continue
twoSum(nums, frist, first+1, len(nums)-1, -nums[first])
def twoSum(nums, start, i, j, target):
res = []
while i < j:
s = nums[i] + nums[j]
if s == target:
res.append(nums[start], nums[i], nums[j])
j -= 1
# 第二层指针右移时去重
while i < j and nums[i] == nums[i+1]:
i += 1
i += 1 # 第二层指针右移
# 第三层指针左移时去重
while i < j and nums[j] == nums[j-1]:
j -= 1
j -= 1 # 第三层指针左移
elif s < target:
i += 1 # 总值太小,第二层指针右移
else:
j -= 1 # 总值太大,第三层指针左移
代码一:twoSum辅助函数版
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
if len(nums) < 3:
return res
n = len(nums)
for first in range(n):
if first > 0 and nums[first] == nums[first-1]:
continue
res.extend(self.twoSum(nums, first, first+1, n-1, -nums[first]))
return res
def twoSum(self, nums: List[int], first: int, second: int, third: int, target:int) -> List[List[int]]:
res = []
while second < third:
s = nums[second] + nums[third]
if s == target:
res.append([nums[first], nums[second], nums[third]])
while second < third and nums[second] == nums[second+1]:
second += 1
second += 1
while second < third and nums[third] == nums[third-1]:
third -= 1
third -= 1
elif s > target:
third -= 1
else:
second += 1
return res
代码二:简洁版twoSum双指针
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
if len(nums) < 3:
return res
n = len(nums)
for first in range(n):
if first > 0 and nums[first] == nums[first-1]:
continue
second = first + 1
third = n - 1
while second < third:
s = nums[first] + nums[second] + nums[third]
if s == 0:
res.append([nums[first], nums[second], nums[third]])
while second < third and nums[second] == nums[second + 1]:
second += 1
second += 1
while second < third and nums[third] == nums[third - 1]:
third -= 1
third -= 1
elif s > 0:
third -= 1
else:
second += 1
return res
小优化:双指针操作前优化双指针区间
在确定了 first、second、third 的指针后,可以判断 nums[first] + nums[second] + nums[third] 是否大于0或者小于0,注意,这里只需要判断一侧即可,即大于0或者小于0,切记不能两边都判断。
- 当sum > 0时,说明保持third指针不变,随着second指针的右移,sum都会大于0,在这个区间是不可能有满足条件的值,所以应该在当前third的左边区间找,third往左移动
- 当sum < 0时,说明保持second指针不变,随着third指针的左移,sum都会小于0,在这个区间是不可能有满足条件的值,所以应该在当前second的右边寻找,second往右移动 从上面的两种方案种,判断一种即可:
代码三:优化版twoSum双指针
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
nums.sort()
if len(nums) < 3:
return res
n = len(nums)
for first in range(n):
if first > 0 and nums[first] == nums[first-1]:
continue
second = first + 1
third = n - 1
if second < third:
while nums[first] + nums[second] + nums[third] > 0 and second < third:
third -= 1
while second < third:
s = nums[first] + nums[second] + nums[third]
if s == 0:
res.append([nums[first], nums[second], nums[third]])
while second < third and nums[second] == nums[second + 1]:
second += 1
second += 1
while second < third and nums[third] == nums[third - 1]:
third -= 1
third -= 1
elif s > 0:
third -= 1
else:
second += 1
return res