leetcode(15). 3Sum

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值