Problem:
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]
]
Idea:
Just refer to the question about 2Sum and 3Sum. We can simplify this Problem into a N-2 Problem.
Solution:
1. Here is a most straight forward solution specially for the Problem 4Sum.
class Solution(object):
def fourSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[List[int]]
"""
lennums = len(nums)
resultlist = []
i = j =0
nums.sort()
if lennums < 4:
return []
for i in xrange(lennums):
for j in xrange(i+1,lennums):
tmpi = i+1
tmpj = j-1
while tmpi < tmpj:
if tmpi >= lennums or tmpj <= 0:
break
tmpresult = nums[i]+nums[j]+nums[tmpi]+nums[tmpj]
if tmpresult < target:
tmpi += 1
elif tmpresult > target:
tmpj -= 1
else:
tmplist = [nums[i],nums[j],nums[tmpi],nums[tmpj]]
resultlist.append(tmplist)
tmpi += 1
tmpj -= 1
func = lambda x,y:x if y in x else x + [y]
return reduce(func,[[],]+resultlist)
2. Better solution for “N-2”Sum Problem. ref link
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
Here are some ways to remove duplicates in List:
1. Check every item to be appended into the list:
mylist = [1,2,3,4,5]
newitem = 3
if newitem not in mylist:
mylist.append(newitem)
2. Use Set
mylist = [1,2,3,4,5]
mylist= list(set(mylist))
Note that if the list item is editable, which means it is not int, string, tuple or something others, a type error may occur:
mylist = [[1,2,3],[2,3,4],[4,5,6]]
newitem = [3,4,5]
mylist = list(set(mylist))
TypeError: unhashable type: ‘list’
3. Use function: “reduce” ref link
In [5]: ids = [1,4,3,3,4,2,3,4,5,6,1]
In [6]: func = lambda x,y:x if y in x else x + [y]
In [7]: reduce(func, [[], ] + ids)
Out[7]: [1, 4, 3, 2, 5, 6]
Here, lambda x,y:x if y in x else x + [y]
means lambda x,y: y in x and x or x+[y]
.
4. A specific method in this Problem
As we use the sorted list in this problem, during constructing the result list, we can skip same numbers in the neighbor.
if nums[l] + nums[r] == target:
results.append(result + [nums[l], nums[r]])
l += 1
r -= 1
#skip same numbers after appending
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