leetcode之三数之和

题目描述:

给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有和为 0 且不重复的三元组。

注意:答案中不可以包含重复的三元组。

示例 1:

输入:nums = [-1,0,1,2,-1,-4]
输出:[[-1,-1,2],[-1,0,1]]
示例 2:

输入:nums = []
输出:[]
示例 3:

输入:nums = [0]
输出:[]
 

提示:

0 <= nums.length <= 3000
-105 <= nums[i] <= 105

解题思路:

核心:降维+双指针法。

1.去重

首先我们可以对数组做初步的处理,三个数字之和等于0,假设这三个数字分别是a,b,c。

a+b+c=0

则所有的组合情况如下:

特殊情况1,三个数字都相等:a=b=c=0

特殊情况2,其中的两个数字相等:a=b=-c,a=c=-b,b=c=-a

一般情况:三个数字都不相同

所以我们可以断定,不同的数字但值相同(0除外)最多只需要出现2次即可,多余的可以省去。

所以可以去重,降低数据的处理量,相关代码如下:

# 让一个数最多出现2次,0最多可以出现3次
        num_dic = defaultdict()
        for i in nums:
            cur_li = num_dic.setdefault(i, [])
            if i != 0:
                if len(cur_li) < 2:
                    cur_li.append(i)
            else:
                if len(cur_li) < 3:
                    cur_li.append(i)

        nums = [val for i in num_dic.values() for val in i]

2.排序

nums.sort()  # 排序

使用sort()进行排序,不用生成新的数组,减小了开销

3.降维+双指针寻找解集

可以将三个数字之和看成两个数字和另外一个特殊的数字之和,因为所有的数字都是出自同一个列表,所以这个特殊的数字也一定在这个列表中。

那么可以通过遍历这个数组,假设遍历的得到的值即为这个特殊的数字target,同时使用双指针遍历数组,头指针得到的值为before,尾指针得到的值为after,那么会出现三种情况:

(目的是头指针指向的值+尾指针指向的值+target=0)

1.头指针指向的值+尾指针指向的值>-target:说明两数之和偏大,则尾指针前移

2.头指针指向的值+尾指针指向的值<-target:说明两数之和偏小,则头指针后移

3.头指针指向的值+尾指针指向的值=-target:说明两数之和等于-target,此时头指针后移或者尾指针前移都行,但是一定要移动一个!不能同时移动两个!

如果是第三种情况,再进行如下判断:

如果头指针指向的数字和target不是同一个数字,且尾指针指向的数字和target不是同一个数字,则构成一个解!

        for i in range(nums_len):
            before, after = 0, nums_len - 1
            target = nums[i]
            while before != after:
                cur_sum = nums[before] + nums[after]
                if cur_sum > -target:  # 偏大,尾指针前移
                    after -= 1
                elif cur_sum < -target:  # 偏小,头指针后移
                    before += 1
                else:
                    if i != before and i != after:  # 不能是同一个数字
                        res_set.add(tuple(sorted((target, nums[before], nums[after]))))  # 转成元组才能hash,才能作为字典的键
                    before += 1 #after-=1是一样的

完整代码:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        if len(nums) < 3:
            return []

        # 降维,遍历数组,遍历到的每一个值设为target,在素组中寻找两个数之和为-target的数
        # 让一个数最多出现2次,0最多可以出现3次
        num_dic = defaultdict()
        for i in nums:
            cur_li = num_dic.setdefault(i, [])
            if i != 0:
                if len(cur_li) < 2:
                    cur_li.append(i)
            else:
                if len(cur_li) < 3:
                    cur_li.append(i)

        nums = [val for i in num_dic.values() for val in i]
        nums.sort()  # 排序
        nums_len = len(nums)
        res_set = set()
        for i in range(nums_len):
            before, after = 0, nums_len - 1
            target = nums[i]
            while before != after:
                cur_sum = nums[before] + nums[after]
                if cur_sum > -target:  # 偏大,尾指针前移
                    after -= 1
                elif cur_sum < -target:  # 偏小,头指针后移
                    before += 1
                else:
                    if i != before and i != after:  # 不能是同一个数字
                        res_set.add(tuple(sorted((target, nums[before], nums[after]))))  # 转成元组才能hash,才能作为字典的键
                    before += 1 #after-=1是一样的
        return [i for i in res_set]

通过截图:

同步更新于个人博客系统: leetcode之三数之和

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值