leetcode 15 三数之和

题目描述

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

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

 

示例:

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

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

解题思路一

参考两数之和的解法,多指针法,两数之和需要左指针和右指针,所以可以合理的推测,三数之和需要用到三个指针。

为了能用上两数之和的结论,首先要固定一个元素,这样问题就变成了两数之和,然后在利用双指针法求解即可。

(1)遍历整个数组,当前位置k

(2)固定位置k在数组中的值,然后按照两数之和的解法,移动左指针右指针,使其对应数组的值相加等于k在数组中的值的相反数。

(3)去重,保证当前三个数字的组合在最后的结果集中仅出现一次

更多细节结合代码展开:

class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        
        res = []
        
        nums.sort() # 排序是必须的,决定了移动左右指针时能不能输出正确的组合
        for k in range(len(nums)-2):
            # nums是从小到大排序的,所以如果第一个元素大于0,其余的元素一定是大于0的,
            # 此时没有继续查找的必要,直接跳出循环
            if nums[k] > 0:
                break
            # 去重,如果当前固定k指针对应值和它前面一个相同,则跳过
            if k > 0 and nums[k] == nums[k-1]:continue
            # 固定k指针后,设置左指针和右指针,和两数之和的设置方式相同,不同的是
            # 左指针的起点
            i = k + 1
            j = len(nums) - 1
            # 基本与两数之和的处理过程相同
            # 不同之处是增加去重操作
            while i < j :
                # 一个有效组合,固定指针k对应的值 + 左指针i对应的值 + 右指针j对应的值
                # curr 代表当前组合相加得到的和
                curr = nums[k] + nums[i] + nums[j]
                print(curr)
                if curr == 0:
                    temp = [nums[k],nums[i],nums[j]]
                    
                    res.append(temp)
                    i += 1
                    j -= 1
                    # 添加一组满足条件的组合后,左指针前右移动一位,
                    # 右指针向左移动一位
                    # 去重,左右指针移动之后和上一个位置的值如果相同,则要继续移动
                    # 保证每次添加到结果集中的组合都是不同的
                    while i < j and nums[i] == nums[i - 1]: i += 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
                    

                elif curr > 0:
                    j -= 1
                    while i < j and nums[j] == nums[j + 1]: j -= 1
                else:
                    i += 1
                    while i < j and nums[i] == nums[i - 1]: i += 1
        return res 
        

解题思路二

依然是参考两数之和的解法,利用字典(哈希表)进行快速查找。

字典的 key-value都是一一对应的,所以需要先固定两个数,然后判断用0减去固定的两个数,判断差是否在字典中,去重输出结果集。

(1)建字典。

数组中三个数字相加等于0

a + b + c = 0

那么一定有

a + b = -c 

then:

hash_map = {-x:i for i,x in enumerate(nums)}

(2)固定两个数,动态检测是否有和这两个数能够成组的第三个数。

(3)去重

结合代码进一步说明

class Solution:
    def threeSum(self,nums: List[int]) -> List[List[int]]:
        res = []
        nums.sort()
        hash_map = {-x:i for i,x in enumerate(nums)}
        # 这个需要额外增加一个用于去重判断的res_hash,
        # 如果直接用 in 判断,则会超时
        res_hash = {}

        # 异常判断
        if len(nums) < 3:
            return res
        
        for i,first in enumerate(nums):
            # 先固定第一个数,如果从小到大排序后的第一个数大于0,
            # 那么其余数必然大于0,直接跳出循环
            if first > 0:
                break
            # 去重,如果下一个位置固定的第一个数和上一个位置一样,则跳过
            # 从后面的元素开始
            if i > 0 and first == nums[i-1]:
                continue
            # 固定第二个数
            for j,second in enumerate(nums[i+1:]):
                # curr表示当前固定的两个数的和
                curr = first + second
                # 判断curr是否在hash_map中
                if curr in hash_map:
                    
                    third_index = hash_map[curr]
                    # 去重,如果此时第三个数的索引和第一个固定位置重叠,或者
                    # 和第二个固定位置重叠,则跳过
                    # 为什么第二个固定位置不是j?
                    # j 是 0 开始的相对位置,对应到原始数组中的索引是 i+1+j
                    if third_index == i or third_index == i+1+j:
                        continue
                    # 排序,是为了保证结果集中的组合都是独一无二的。
                    row = sorted([first,second,nums[third_index])
                    # 如果直接用:
                    # if row not in res:
                    #     res.append(row)
                    # return res 
                    # 提交后会超时,这是因为列表的in操作速度比字典的in判断速度慢得多,
                    # 尤其是列表中元素较多时。详情可以看下这两个in的原理
                    key = ''.join([str(x) for x in row])
                    if key not in res_hash:
                        res.append(row)
                        res_hash[key] = True
        return res
                
思路一解法执行用时/内存消耗思路二解法执行用时/内存消耗
2660ms/16.8MB504ms/18.2MB

得益于字典的快速查找,思路二解法比思路一快了将近5倍,内存消耗比思路一多了差不多2MB多一点。

 

 

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/3sum
 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

nobrody

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值