题目在这​​:https://leetcode-cn.com/problems/3sum/​

思路分析:

这题暴力可以到 315/318 个用例。

使用三指针法。
首先对数组进行排序。
设置一个遍历指针k。

设置i指针和j指针分别指向k指针的后一个指针和数组尾。
这样的话, 指针大致位置是 k–>i–>j。

  • 如果nums[k]大于0 ,则显然 nums[i]和nums[j]都大于0,则必然不会出现三者相加等于0的情况。
  • 如果nums[k]和nums[k-1] 相等,则说明已经遍历过这个位置了,也就是说已经有这个三元组了。 这里要注意为啥是[k]和[k-1],为啥不是[k]和[k+1],可以想象有一个数组[-1,-1,2]如果是[k]和[k+1]比较,那就是k指针和i指针进行比较了,则就是判断三元组里是不是有重复的元素了,这并不符合题意,题意是要求没有重复的三元组,而在三元组内的值是可以重复的。
  • 如果num[k]和num[k-1]不相等, 设 nums[k]+nums[i]+nums[j] = s
  • 若s = 0,符合题意,将此时结果记录至答案集,然后让 i往后挪,j往前挪,且跳过所有相同的值。(这里必须是同时挪动,简单想一下,此时三者相加答案为0,如果只挪动一边,且跳过了所有的重复项,答案必不可能再次为0.)
  • 若s > 0, 说明此时需要找到更小的组合,将j往前挪,跳过所有相同的j指向的值。
  • 若s < 0,说明此时需要找到更大的组合,将i往后挪,跳过所有相同 的i指向的值。

按照上面的情况写一堆循环+判断就行了。

完整代码

class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
res = []
if len(nums) <=2:
return []
nums.sort()
for k in range(len(nums)-2):
i = k+1
j = len(nums)-1
if nums[k] > 0:
break
if k!=0 and nums[k] == nums[k-1]:
continue
while i < j:
if nums[k] + nums[i] + nums[j] == 0:
res.append([nums[k],nums[i],nums[j]])
i +=1
j -=1
while nums[i] == nums[i-1] and i < j: # 跳过所有相同的i指向
i +=1
while nums[j] == nums[j+1] and i <j: # 跳过所有相同的j指向
j -=1
elif nums[k] + nums[i] + nums[j] <0 :
i +=1
while nums[i] == nums[i-1] and i < j:
i +=1
elif nums[k] + nums[i] + nums[j] > 0:
j -=1
while nums[j] == nums[j+1] and i <j:
j -=1

return

直接顺带说一下​​16. 最接近的三数之和​​​。
这道题其实我觉得是上面这道题的简化版,我们不需要考虑那么多的情况了,相等的情况也不用考虑了。
依旧使用上面题的三指针法。
每次求出来三个数的和与目标值的差值的绝对值,并将最小的那个记录下来。
如果三数之和大于目标值,则我们减小一点三数之和,即将 j 往左挪。
如果三数之和小于目标值,则我们扩大一点三数之和,即将 i 往右挪。
目标就是通过不断的变化指针,找到和目标值最接近的三数之和。

完整代码:

class Solution:
def threeSumClosest(self, nums: List[int], target: int) -> int:

res = 0
temp = 99999
nums.sort()
for k in range(len(nums)):
i = k +1
j = len(nums) -1
while i < j:
sum = nums[i] + nums[j] +nums[k]
if abs(sum-target) < temp:
temp = abs(sum-target)
res = sum
if sum < target:
i +=1
elif sum > target:
j -=1
elif sum == target:
return sum
print(res)
return