problem
Given an array S of n integers, are there elements a, b, c in S such
that a + b + c = 0? Find all unique triplets in the array which gives
the sum of zero.Note: The solution set must not contain duplicate triplets.
For example, given array S = [-1, 0, 1, 2, -1, -4],
A solution set is: [ [-1, 0, 1], [-1, -1, 2] ]
solution
my solution
总结
类似于two sum中的思路,遍历所有
C2n
,利用hash表找出-nums[i]-nums[j]
,时间复杂度为
O(n2)
,需要注意的就是不要放入重复的组合,例如[-1, 1, 0]和[-1, 1, 0],这里采用的方法是把所有的已得到结果放入set中,然后将结果排序后add。
如何比较两个list是否相同,可以将两个list排列之后逐个比较,这样复杂度较大,比较高效的算法可以这样,将两个list加入hash表中,key为元素值,value为出现次数,然后先比较不同的元素个数是否相同,若相同则逐个比较每个元素出现次数是否相同,时间复杂度为 O(n) 。
def eqList(l1, l2):
if len(l1) != len(l2):
return False
counter = Counter(l1)
for i in l2:
counter[i] -= 1
if counter[i] < 0:
return False
return True
class Solution(object):
def threeSum(self, nums):
"""
:type nums: List[int]
:rtype: List[List[int]]
"""
n = len(nums)
d = dict()
ans = set()
tset = set()
for i in range(n):
if nums[i] in d:
d[nums[i]].append(i)
else:
d[nums[i]] = [i]
for i in range(n):
for j in range(i+1, n):
ttuple = tuple(sorted([nums[i], nums[j]]))
if ttuple in tset:
continue
else:
tset.add(ttuple)
if -nums[i]-nums[j] in d:
for tmp in d[-nums[i]-nums[j]]:
if tmp > j:
ans.add(tuple(sorted((nums[i], nums[j], nums[tmp]))))
break
return list(ans)
discussion
这是在leetcode里面的discussion里面看到的方法,感觉虽然和上面一样是 O(n2) 但是因为不用取出相同的组合所以效率更高。
def threeSum(self, nums):
res = []
nums.sort()
for i in xrange(len(nums)-2):
if i > 0 and nums[i] == nums[i-1]:
continue
l, r = i+1, len(nums)-1
while l < r: #有序数组twoSum
s = nums[i] + nums[l] + nums[r]
if s < 0:
l +=1
elif s > 0:
r -= 1
else:
res.append((nums[i], nums[l], nums[r]))
#把l和r向中间移动,越过相同的元素,继续寻找不同的twoSum组合
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
这个算法的巧妙之处就在于对nums进行排序,然后使用two-pointer方法进行2sum,这样还可以避免重复的组合。
while l < r:
s = nums[i] + nums[l] + nums[r]
if s < 0:
l +=1
elif s > 0:
r -= 1
else:
res.append((nums[i], nums[l], nums[r]))
可以抽象为在一个数组nums中找到两个和为0的数字
nums.sort()
while l < r:
s = nums[l] + nums[r]
if s < 0:
l +=1
elif s > 0:
r -= 1
else:
res.append((nums[l], nums[r]))
正确性证明:
可以考虑为递归问题:
若nums首位元素相加小于target,则第一个元素肯定不是解,所以把问题转化为twoSum(nums[1:], target),大于的话同理。
这里的最关键的一个点就是为什么当s < 0
时只需要 l += 1
而不是在r += 1
方向也寻找(s > 0同理),因为r就是从右边移过来的,这说明nums[r+1]
对nums[l]
及之前的元素肯定有元素使得两个数的和大于零,所以再nums[r+1]
对nums[l]
及之后的元素肯定都大于零,所以不必r += 1