leetcode18四数之和
给你一个由 n
个整数组成的数组 nums ,和一个目标值 target
。请你找出并返回满足下述全部条件且不重复的四元组 [nums[a], nums[b], nums[c], nums[d]]
(若两个四元组元素一一对应,则认为两个四元组重复):
0 <= a, b, c, d < n
a
、b
、c
和d
互不相同nums[a] + nums[b] + nums[c] + nums[d] == target
你可以按 任意顺序 返回答案 。
思路与三数之和类似,使用指针解决。我写代码遇到与三数之和不同点是第二层i循环剪枝操作
# 第二层剪枝
for i in range(k+1, n-2):
if nums[k]+nums[i] > target and nums[k]+nums[i] > 0:
break
与第一层k循环剪枝操作不同
# 第一层剪枝
for k in range(n-3):
if nums[k] > target and nums[k] > 0:
return ans
第一层剪枝可以直接返回,原因是
正数nums[k]一旦大于target,那么它后面的nums[i],nums[left],nums[right] 都会是正数,四数相加一定大于target,因此没有进入循环的必要。
然而第二层循环不可以直接返回,原因是
虽然nums[k]+nums[i]大于target且为正数,下一轮nums[k]大于这一轮nums[k],但是我们不能确定nums[i]的大小,因为i也在进行循环。比如nums=[-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5],一轮中nums[k]=-2,nums[i]=0,nums[left]=0,nums[right]=2,target=0,我们找到了四元组[-1, 0, 0, 1]。i继续循环,直到nums[i]=3,此时满足nums[k]+nums[i]=1大于target且为正数,但是我们不能确定下一轮nums[k]+nums[i]与本轮nums[k]+nums[i]的大小。例如下一轮次nums[k]=-1,nums[i]=0,相加等于-1,并没有大于上一轮结束时nums[k]+nums[i]。
出现这种情况的主要原因是在一轮k循环中nums[k]是固定的的,但nums[i]是变化的,它可以在一轮k循环中变得很大,但是下一轮k循环又会重新变小。因此直接return会丢失可能的四元组,应该使用break。
完整代码如下:
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
n = len(nums)
if n < 4:
return []
ans = []
nums.sort()
for k in range(n-3):
if nums[k] > target and nums[k] > 0:
return ans
if k > 0 and nums[k] == nums[k-1]:
continue
for i in range(k+1, n-2):
if i > k+1 and nums[i] == nums[i-1]:
continue
if nums[k]+nums[i] > target and nums[k]+nums[i] > 0:
break
left = i + 1
right = n - 1
while left < right:
if nums[k]+nums[i]+nums[left]+nums[right] == target:
ans.append([nums[k], nums[i], nums[left], nums[right]])
while left < right and nums[right] == nums[right-1]:
right -= 1
while left < right and nums[left] == nums[left+1]:
left += 1
left += 1
right -= 1
elif nums[k]+nums[i]+nums[left]+nums[right] > target:
right -= 1
else:
left += 1
return ans