- 题目描述
给定一个包含 n 个整数的数组 nums 和一个目标值 target,判断 nums 中是否存在四个元素 a,b,c 和 d ,使得 a + b + c + d 的值与 target 相等。找出所有满足条件且不重复的四元组。
注意:
答案中不可以包含重复的四元组。
- 示例
给定数组 nums = [1, 0, -1, 0, -2, 2],和 target = 0。
满足要求的四元组集合为: [ [-1, 0, 0, 1], [-2, -1, 1, 2], [-2, 0, 0, 2] ]
- 思路一
借鉴讨论中的一种思路,把四数之和变为三数之和问题,target就变为target-nums[i]。但是对三数之和的代码进行拓展修改后,提交显示超时。
- 代码一
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
def threeSum(nums,target1):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
if(len(nums)) < 3:
return []
#对数组排序
nums.sort()
#要返回的集合
sets = []
#遍历数组
for i in range(len(nums)-2):
#不可以用这种方式来进行判断
# if (nums[i] == nums[i+1]):
# continue
#原因在于i前边的元素肯定是参与过运算的,如果i和前一个相同,那么就没有必要再进行一次对j和k的遍历(第二个数和第三个数)
#而i后边的元素是没有作为三元组的第一个数存在过的,如果判断i和i+1的大小的话,会忽略掉j = i+1的情况
#其实是对第一个数字的控制,判断当前i对应的数字是否作为第一个数参与过遍历计算。如果判断i与i+1,则达不到这样的作用
if(i>0 and nums[i] == nums[i-1]):
continue
#j是前边的指针
j = i +1
#k是后边的指针
k = len(nums) - 1
while(j<k):
rr = nums[i]+nums[j]+nums[k]
#如果三数之和等于0
if(rr==target1):
sets.append([nums[i],nums[j],nums[k]])
#考虑前边的指针遍历到相同的数值
while(j<k and nums[j] == nums[j+1]):
j += 1
#考虑后边的指针遍历到相同的数值
while(j<k and nums[k] == nums[k-1]):
k -= 1
j += 1
k -= 1
#如果三数之和小于0,那么前边的指针向后移动(j指示的元素数值变大)
elif(rr<target1):
j += 1
#如果三数之和大于0,那么后边的指针向前移动(k指示的元素数值变小)
else:
k -= 1
return sets
a = []
for i in range(len(nums)):
t = copy.deepcopy(nums)
t.pop(i)
l = threeSum(t,target - nums[i])
for j in l:
j.append(nums[i])
j.sort()
a.append(j)
ans = []
[ans.append(i) for i in a if i not in ans]
return ans
- 思路二
三数之和的思路是固定第一个数,求两数之和;以此类推,四数之和是固定第一个数,求三数之和。那么就是在三数之和的基础上多加一层循环。根据三数之和中去重和根据当前和与target的差移动前指针和后指针的思想,据此来写四数之和。
- 代码二
class Solution:
def fourSum(self, nums: List[int], target: int) -> List[List[int]]:
#对数组进行排序
nums.sort()
#求数组长度
l = len(nums)
#要返回的结果列表
res = []
#遍历数组,第一个数字
for i in range(l-3):
#去重
if i == 0 or nums[i] > nums[i-1]:
#遍历数组,第二个数字
for j in range(i+1,l-2):
#去重
if j == i + 1 or nums[j] > nums[j-1]:
#第三个数字
left = j + 1
#第四个数字
right = l - 1
#在剩余数字的范围内,判断左右指针如何移动
while left < right:
#当前的四数之和
indent = nums[i]+ nums[j] + nums[left] + nums[right]
#判断当前的四数之和与target的关系
if indent == target:
#相等,则在结果列表中加入当前的四个数,左指针向右移,右指针向左移
res.append([nums[i],nums[j],nums[left],nums[right]])
left += 1
right -= 1
#移动过程中去重
while left < right and nums[left] == nums[left-1]:
left += 1
while left < right and nums[right] == nums[right+1]:
right -= 1
#如果当前四数之和大于target,则需要减小当前的和,把右指针向左移
elif indent > target:
right -= 1
#否则需要增大当前的和,把左指针向右移
else:
left += 1
return res