给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等?找出所有满足条件且不重复的四元组。
注意:答案中不可以包含重复的四元组。
示例 1:
输入:nums = [1,0,-1,0,-2,2], target = 0
输出:[[-2,-1,1,2],[-2,0,0,2],[-1,0,0,1]]
示例 2:
输入:nums = [], target = 0
输出:[]
解法
排序+双指针 本题和15. 三数之和类似,解题思路也和三数之和一样。
主要思想:先用for遍历,列举前面两个数,然后用双指针列举最后两个数。
自己没看官方答案的实现如下:
def fourSum1(nums, target):
n = len(nums)
if n < 4: return []
nums.sort()
res = [] # 记录返回的数组
# 枚举a
for i in range(n):
# 保证和上一次枚举的元素不相等
# 这句代码不能要,因为有负数的情况,比如 nums=[1, -2, -5, -4, -3, 3, 3, 5] target=-11
# if nums[i] > target: return res
if i > 0 and nums[i] == nums[i - 1]: # i>0 是要保证第一个数是参与运算了的
continue
for j in range(i+1, n):
if j > i+1 and nums[j] == nums[j - 1]: # j > i+1 是要保证j=i+1 那个数是参与运算了的
continue
# 使用双指针枚举b, c
L, R = j + 1, n - 1
while L < R:
sum = nums[i] + nums[j] + nums[L] + nums[R]
# 如果和为target,那么它就是最接近的和,直接返回
if sum == target:
res.append([nums[i], nums[j], nums[L], nums[R]]) # 当满足四数之和等于target,就记录下来
while L < R and nums[L] == nums[L + 1]: L += 1 # 去除左边的重复元素
while L < R and nums[R] == nums[R - 1]: R -= 1 # 去除右边的重复元素
L += 1 # 满足了一组三数之和等于0之后,左右指针就向中间都移动一位,寻找新的组合
R -= 1
elif sum < target:
# 如果和小于target,b 对应的指针向右移动一位
L += 1
else:
# 如果和大于target,c 对应的指针向左移动一位
R -= 1
return res
官方答案比我的更加优化,时间复杂度一样,只是增加了一些剪枝操作:
-
在确定第一个数之后,如果
nums[i]+nums[i+1]+nums[i+2]+nums[i+3]>target,说明此时剩下的三个数无论取什么值,四数之和一定大于target,因此退出第一重循环; -
在确定第一个数之后,如果
nums[i]+nums[n−3]+nums[n−2]+nums[n−1]<target,说明此时剩下的三个数无论取什么值,四数之和一定小于target,因此第一重循环直接进入下一轮,枚举nums[i+1]; -
在确定前两个数之后,如果
nums[i]+nums[j]+nums[j+1]+nums[j+2]>target,说明此时剩下的两个数无论取什么值,四数之和一定大于target,因此退出第二重循环; -
在确定前两个数之后,如果
nums[i]+nums[j]+nums[n−2]+nums[n−1]<target,说明此时剩下的两个数无论取什么值,四数之和一定小于target,因此第二重循环直接进入下一轮,枚举nums[j+1]。
def fourSum2(nums, target):
quadruplets = list()
if not nums or len(nums) < 4:
return quadruplets
nums.sort()
length = len(nums)
for i in range(length - 3):
if i > 0 and nums[i] == nums[i - 1]:
continue
if nums[i] + nums[i + 1] + nums[i + 2] + nums[i + 3] > target:
break
if nums[i] + nums[length - 3] + nums[length - 2] + nums[length - 1] < target:
continue
for j in range(i + 1, length - 2):
if j > i + 1 and nums[j] == nums[j - 1]:
continue
if nums[i] + nums[j] + nums[j + 1] + nums[j + 2] > target:
break
if nums[i] + nums[j] + nums[length - 2] + nums[length - 1] < target:
continue
left, right = j + 1, length - 1
while left < right:
total = nums[i] + nums[j] + nums[left] + nums[right]
if total == target:
quadruplets.append([nums[i], nums[j], nums[left], nums[right]])
while left < right and nums[left] == nums[left + 1]:
left += 1
left += 1
while left < right and nums[right] == nums[right - 1]:
right -= 1
right -= 1
elif total < target:
left += 1
else:
right -= 1
return quadruplets
复杂度分析
-
时间复杂度:,其中 n 是数组的长度。排序的时间复杂度是 ,枚举四元组的时间复杂度是 ,因此总时间复杂度为 。
-
空间复杂度:,其中 n 是数组的长度。空间复杂度主要取决于排序额外使用的空间。此外排序修改了输入数组 nums,实际情况中不一定允许,因此也可以看成使用了一个额外的数组存储了数组 nums 的副本并排序,空间复杂度为 。