代码链接:https://leetcode.cn/problems/3sum/solution/3sumpai-xu-shuang-zhi-zhen-yi-dong-by-jyd/
解题思路:三指针
暴力法搜索为 O ( N 3 ) O(N^3) O(N3)时间复杂度,可通过双指针动态消去无效解来优化效率。
双指针法铺垫: 先将给定 nums 排序,复杂度为 O ( N l o g N ) O(NlogN) O(NlogN)。
双指针法思路: 固定 3 个指针中最左(最小)数字的指针 k k k,双指针 i , j i,j i,j 分设在数组索引 (k+1, len(nums)-1) \text{(k+1, len(nums)-1)} (k+1, len(nums)-1) 两端,通过双指针交替向中间移动,记录对于每个固定指针 k k k 的所有满足 n u m s [ k ] + n u m s [ i ] + n u m s [ j ] = = 0 nums[k] + nums[i] + nums[j] == 0 nums[k]+nums[i]+nums[j]==0 的 i , j i, j i,j 组合:
- 当 n u m s [ k ] > 0 nums[k] > 0 nums[k]>0 时直接break跳出:因为 n u m s [ j ] ≥ n u m s [ i ] ≥ n u m s [ k ] > 0 nums[j] \geq {nums[i]} \geq {nums[k] > 0} nums[j]≥nums[i]≥nums[k]>0,即 3 个数字都大于 0,在此固定指针 k k k 之后不可能再找到结果了。
- 当 k > 0 k > 0 k>0且 n u m s [ k ] = = n u m s [ k − 1 ] nums[k] == nums[k - 1] nums[k]==nums[k−1]时即跳过此元素 n u m s [ k ] nums[k] nums[k]:因为已经将 n u m s [ k − 1 ] nums[k - 1] nums[k−1] 的所有组合加入到结果中,本次双指针搜索只会得到重复组合。
i , j i, j i,j分设在数组索引 ( k + 1 , l e n ( n u m s ) − 1 ) (k+1, len(nums)-1) (k+1,len(nums)−1) 两端,当 i < j i < j i<j 时循环计算 t o t a l = n u m s [ k ] + n u m s [ i ] + n u m s [ j ] total = nums[k] + nums[i] + nums[j] total=nums[k]+nums[i]+nums[j],并按照以下规则执行双指针移动:
- 当 t o t a l < 0 total < 0 total<0时, i + = 1 i += 1 i+=1;(不需要跳过重复数字,下一轮循环条件不会满足,自动跳出)
- 当 t o t a l > 0 total > 0 total>0时, j − = 1 j -= 1 j−=1;
- 当 t o t a l = = 0 total == 0 total==0时,记录组合 [ k , i , j ] [k, i, j] [k,i,j]至 r e s res res,执行 i + = 1 i += 1 i+=1 和 j − = 1 j -= 1 j−=1 并跳过所有重复的 n u m s [ i ] nums[i] nums[i]和 n u m s [ j ] nums[j] nums[j],防止记录到重复组合。
复杂度分析:
- 时间复杂度 O ( N 2 ) O(N^2) O(N2):其中固定指针 k k k 循环复杂度 O ( N ) O(N) O(N),双指针 i , j i, j i,j 复杂度 O ( N ) O(N) O(N)。
- 空间复杂度 O ( 1 ) O(1) O(1):指针使用常数大小的额外空间。
class Solution:
def threeSum(self, nums: List[int]) -> List[List[int]]:
nums.sort() # 对数组进行排序
res = []
for k in range(len(nums)-2): # 固定一个数
# nums[j]≥nums[i]≥nums[k]>0,即3个数字都大于0
if nums[k] > 0:
break
# 跳过此元素nums[k]:因为已经将nums[k - 1]的所有组合加入到结果中
if k > 0 and nums[k] == nums[k-1]:
continue
# k每遍历一次数组,i和j从k+1 -> len(nums)-1移动一次
i, j = k+1, len(nums)-1
while i < j:
total = nums[k] + nums[i] + nums[j] # 求三个数的和
if total > 0: # 值大于0, j前移
j -= 1
elif total < 0: # 值小于0, i后移
i += 1
else: # 值=0
res.append([nums[k], nums[i], nums[j]])
# 如果临近数值相同,则i指针后移
while i < j and nums[i] == nums[i+1]:
i += 1
# 如果临近数值相同,则j指针前移
while i < j and nums[j] == nums[j-1]:
j -= 1
i += 1 # 移动指针,查找其它可能结果
j -= 1
return res