Given an array S of n integers, are there elements a, b, c, and d in S such that a + b + c + d = target? Find all unique quadruplets in the array which gives the sum of target.
Note: The solution set must not contain duplicate quadruplets.
For example, given array S = [1, 0, -1, 0, -2, 2], and target = 0.
A solution set is:
[
[-1, 0, 0, 1],
[-2, -1, 1, 2],
[-2, 0, 0, 2]
]
题意:从数组中找到4个数,使它们的和为target。要求去重,可能有多组解,需要都找出来。
解题思路:一开始想要像3Sum那样去解题,时间复杂度为O(N^3),可无论怎么写都是Time Limited Exceeded。而同样的算法使用C++是可以通过的。说明Python的执行速度比C++慢很多。还说明了一点,大概出题人的意思不是要让我们去像3Sum那样去解题,否则这道题就出的没有意义了。这里参考了kitt的解法:http://chaoren.is-programmer.com/posts/45308.html
需要用到哈希表的思路,这样可以空间换时间,以增加空间复杂度的代价来降低时间复杂度。首先建立一个字典dict,字典的key值为数组中每两个元素的和,每个key对应的value为这两个元素的下标组成的元组,元组不一定是唯一的。如对于num=[1,2,3,2]来说,dict={3:[(0,1),(0,3)], 4:[(0,2),(1,3)], 5:[(1,2),(2,3)]}。这样就可以检查target-key这个值在不在dict的key值中,如果target-key在dict中并且下标符合要求,那么就找到了这样的一组解。由于需要去重,这里选用set()类型的数据结构,即无序无重复元素集。最后将每个找出来的解(set()类型)转换成list类型输出即可。
方法一:222ms
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
ln = len(nums)
res = set()
dict = {}
if ln < 4:
return []
nums.sort()
for p in range(ln):
for q in range(p+1, ln):
if nums[p] + nums[q] not in dict:
dict[nums[p] + nums[q]]=[(p, q)]
else:
dict[nums[p] + nums[q]].append((p, q))
for i in range(ln):
for j in xrange(i+1, ln-2):
T = target - nums[i] - nums[j]
if T in dict:
for k in dict[T]:
if k[0] > j:
res.add((nums[i], nums[j], nums[k[0]], nums[k[1]]))
return [list(i) for i in res]
方法二:132ms
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
ret = []
nums.sort()
length = len(nums)
if length < 4:
return []
for item_idx in xrange(length - 3):
if target < nums[item_idx] * 4 or target > nums[-1] * 4:
break
if item_idx > 0 and nums[item_idx - 1] == nums[item_idx]:
continue
item_idx_base = item_idx + 1
for i in xrange(item_idx_base, length - 2):
if target - nums[item_idx] < nums[i] * 3 or target - nums[item_idx] > nums[-1] * 3:
break
if i > item_idx_base and nums[i - 1] == nums[i]:
continue
left = i + 1
right = length - 1
while (left < right):
sum_val = (nums[item_idx] + nums[i] + nums[left] + nums[right]) - target
if sum_val < 0:
left += 1
elif sum_val > 0:
right -= 1
else:
ret.append([nums[item_idx], nums[i], nums[left], nums[right]])
while(left < right and nums[left] == nums[left + 1]):
left += 1
left += 1
return ret
方法三:103ms
这个方法很厉害啊。。。findNSum
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
# res = []
# nums.sort()
# for i in range(len(nums) - 3):
# if i > 0 and nums[i] == nums[i - 1]:
# continue
# flag = 0
# for j in range(i + 1, len(nums) - 2):
# if flag == 1 and nums[j] == nums[j - 1]:
# continue
# flag = 1
# l, r = j + 1, len(nums) - 1
# while l < r:
# s = nums[i] + nums[j] + nums[l] + nums[r]
# if s < target:
# l += 1
# elif s > target:
# r -= 1
# else:
# res.append([nums[i], nums[j],nums[l], nums[r]])
# while l < r and nums[l] == nums[l + 1]:
# l += 1
# while l < r and nums[r] == nums[r - 1]:
# r -= 1
# l += 1
# r -= 1
# return res
def findNSum(nums, target, N, result, results):
if len(nums) < N or N < 2 or target < nums[0] * N or target > nums[-1] * N:
return
if N == 2:
l, r = 0, len(nums) - 1
while l < r:
s = nums[l] + nums[r]
if s > target:
r -= 1
elif s < target:
l += 1
else:
results.append(result + [nums[l], nums[r]])
while l < r and nums[l] == nums[l + 1]:
l += 1
while l < r and nums[r] == nums[r - 1]:
r -= 1
l += 1
r -= 1
else:
for i in range(len(nums) - N + 1):
if i == 0 or (i > 0 and nums[i] != nums[i - 1]):
findNSum(nums[i+1:], target-nums[i], N - 1, result + [nums[i]], results)
results = []
findNSum(sorted(nums), target, 4, [], results)
return results
class Solution(object):
def fourSum(self, nums, target):
nums.sort()
results = []
self.findNsum(nums, target, 4, [], results)
return results
def findNsum(self, nums, target, N, result, results):
if len(nums) < N or N < 2: return
# solve 2-sum
if N == 2:
l,r = 0,len(nums)-1
while l < r:
if nums[l] + nums[r] == target:
results.append(result + [nums[l], nums[r]])
l += 1
r -= 1
while l < r and nums[l] == nums[l - 1]:
l += 1
while r > l and nums[r] == nums[r + 1]:
r -= 1
elif nums[l] + nums[r] < target:
l += 1
else:
r -= 1
else:
for i in range(0, len(nums)-N+1): # careful about range
if target < nums[i]*N or target > nums[-1]*N: # take advantages of sorted list
break
if i == 0 or i > 0 and nums[i-1] != nums[i]: # recursively reduce N
self.findNsum(nums[i+1:], target-nums[i], N-1, result+[nums[i]], results)
return