Leet Code 015 Three Sum

给定一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?找出所有满足条件且不重复的三元组。
注意:答案中不可以包含重复的三元组。

例如, 给定数组 nums = [-1, 0, 1, 2, -1, -4],

满足要求的三元组集合为:
[
[-1, 0, 1],
[-1, -1, 2]
]

面试中 优先考虑双指针,并使用set保存res,即可以不考虑去重

思路一:利用twoSum
遍历nums,然后转换为twoSum问题,但是要排除重复结果,判重操作时间O(n),twoSum本身时间O(n),空间O(n),总体时间复杂度O(n^3),空间复杂度O(n)

class Solution(object):  # 此法也超时
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        def twoSum(nums, target):
            lookup = {}
            for num in nums:
                if target - num in lookup:
                    if (-target ,target - num, num) not in res:
                        res.append((-target ,target - num, num))
                lookup[num] = target - num

        n = len(nums)
        nums.sort()
        res = []
        for i in range(n):
            twoSum(nums[i+1:], 0-nums[i])
        return [list(i) for i in res]

思路二:暴力法
先给nums排序,满足结果从小到大排列,三层循环

class Solution(object):
    def threeSum(self, nums):
        n = len(nums)
        res = []
        nums.sort()
        for i in range(n):
            for j in range(i,n):
                for k in range(j,n):
                    if nums[i] + nums[j] + nums[k] == 0 and j != i and k != j and k != i: 
                        curRes = [nums[i],nums[j],nums[k]]
                        if curRes not in res:
                            res.append(curRes)
    
        return res

思路三:优化二
两个for loop枚举a,c,则b=-a-c,然后去dyct中查询-a-c是否存在。利用set保存res,达到去重效果。
整体时间复杂度O(n^2),空间复杂度O(n)

def threeSum(nums):
    if len(nums) < 3:
        return []
    nums.sort()
    res = set()
    for i,a in enumerate(nums[:-2]):
        if i >= 1 and a == nums[i-1]:
#             两个连续的值相等时,已经处理过了,跳过此次循环,
            continue
        if v > 0: break  ##因为已经排序了,所以到大于零的位置,之后的数字都是大于零的,肯定没有符合条件的组合了

        dyct = {}
        for c in nums[i+1:]:
            if c not in dyct:
                dyct[-a-c] = None
            else:
                res.add((a, -a-c, c))
    return list(map(list, res))
threeSum([-1, 0, 1, 2, -1, -4])
[[-1, -1, 2], [-1, 0, 1]]

思路四:双指针
排序好nums,遍历nums,对nums[i+1:],使用两个指针l,r分别从左右开始,计算当前数nums[i]和nums[l],nums[r]之和s,若s小于0,由于nums是排好序的,只需l右移,若s大于0则只需r左移,若等于0,则将nums[i],nums[l],nums[r]存入res列表,并左移r 右移l 继续比较。直至两个指针发生碰撞。
注意点:对于相邻重复的元素,需要跳过。
由于需要遍历nums,且双指针遍历nums[i+1:],则总体时间复杂度为O(n^2),空间复杂度为O(n)

def threeSum(nums):
    '''
    使用set保存res,不需要考虑判重,简单直接,思路清晰
    '''
    res = set()
    nums.sort()
    for i in range(len(nums)-2):
        l,r = i+1,len(nums)-1
        while l < r:
            s= nums[i] + nums[l] + nums[r]
            if s < 0:
                l += 1
            elif s >0:
                r -= 1
            else:
                res.add((nums[i],nums[l],nums[r]))
                l += 1
                r -= 1
    return [list(i) for i in res]
threeSum([-1, 0, 1, 2, -1, -4])
[[-1, -1, 2], [-1, 0, 1]]
threeSum([0,0,0,0,0,0,0,0,0])
[[0, 0, 0]]

充分考虑nums结构,进行判重,减少操作,减少时间,加大代码复杂度
且利用排序好的nums,可知当nums[i]>0时,i,i+1,i+2三个元素都大于零,即i以后的元素不存在解。

def threeSum(nums):
    '''
    使用list保存res,在过程中判重。
    '''
    res = []
    nums.sort()
    for i in range(len(nums)-2):
        if i>0 and nums[i] == nums[i-1]: # 两个连续的值相等时,已经处理过了,跳过此次循环,
            continue
        if nums[i]>0: break #因为已经排序了,所以到大于零的位置,之后的数字都是大于零的,肯定没有符合条件的组合了
        l,r = i+1,len(nums)-1
        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]))
                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
threeSum([-1, 0, 1, 2, -1, -4])
[(-1, -1, 2), (-1, 0, 1)]

执行用时 : 844 ms, 在3Sum的Python3提交中击败了88.77% 的用户
内存消耗 : 16.4 MB, 在3Sum的Python3提交中击败了97.63% 的用户

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
探险家小扣的行动轨迹,都将保存在记录仪中。expeditions[i] 表示小扣第 i 次探险记录,用一个字符串数组表示。其中的每个「营地」由大小写字母组成,通过子串 -> 连接。例:"Leet->code->Campsite",表示到访了 "Leet"、"code"、"Campsite" 三个营地。expeditions[0] 包含了初始小扣已知的所有营地;对于之后的第 i 次探险(即 expeditions[i] 且 i > 0),如果记录中包含了之前均没出现的营地,则表示小扣 新发现 的营地。 请你找出小扣发现新营地最多且索引最小的那次探险,并返回对应的记录索引。如果所有探险记录都没有发现新的营地,返回 -1。注意: 大小写不同的营地视为不同的营地; 营地的名称长度均大于 0。用python实现。给你几个例子:示例 1: 输入:expeditions = ["leet->code","leet->code->Campsite->Leet","leet->code->leet->courier"] 输出:1 解释: 初始已知的所有营地为 "leet" 和 "code" 第 1 次,到访了 "leet"、"code"、"Campsite"、"Leet",新发现营地 2 处:"Campsite"、"Leet" 第 2 次,到访了 "leet"、"code"、"courier",新发现营地 1 处:"courier" 第 1 次探险发现的新营地数量最多,因此返回 1。示例 2: 输入:expeditions = ["Alice->Dex","","Dex"] 输出:-1 解释: 初始已知的所有营地为 "Alice" 和 "Dex" 第 1 次,未到访任何营地; 第 2 次,到访了 "Dex",未新发现营地; 因为两次探险均未发现新的营地,返回 -1
最新发布
04-23

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值