前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >三数之和怎么求?LeetCode 15、16 题记

三数之和怎么求?LeetCode 15、16 题记

作者头像
TTTEED
发布2020-07-08 19:50:26
8120
发布2020-07-08 19:50:26
举报

越刷题越觉得自己进度慢、且要补的知识点越多了,所以加快下刷题进度吧。恰好接下来的 15 和 16 题都与三数之和相关,放到一起来记录下。

题目一

第 15 题 三数之和:

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

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

示例:

代码语言:javascript
复制
给定数组 nums = [-1, 0, 1, 2, -1, -4],

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

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

思路

最初尝试了下遍历,穷举所有三元组,求和判断是否为 0,再记录不重复的结果,提交后一直超出时间限制。

联想到之前盛水容器那道题中的双指针法,可以基于判断有选择地避开不必要的穷举,于是在本题中应用双指针法来找和为 0 的三元组:遍历数组列表中的元素作为三元组的第一个,要求的三元组剩余两元素即双指针的值,双指针位于取值范围两端来缩小。

当得到和为 0 的三元组后,因为题目要求不能重复,所以要先检查下结果的列表中是否已经有该三元组:

代码语言:javascript
复制
# lst 为和为 0 的三元组
lst = [num_sort[i],num_sort[x],num_sort[y]]
# result 为结果列表,先判断是否包含该 lst,若没有再添加到结果中
if lst not in result:
    result.append(lst)

但这么一路写下来,提交结果仍然是“超出时间限制”,猜测原因是对三元组是否已经存在的检查拖了后腿。最后,借鉴了一份题解代码,对重复三元组的处理从三元出发,当任一元出现重复时,直接忽略掉,最终总算顺利完成,我们结合着代码和注释来细看。

代码

代码语言:javascript
复制
class Solution:
    def threeSum(self, nums: List[int]) -> List[List[int]]:
        # 为了方便指针定位以及对三元处理,现将数组列表排序
        num_sort = sorted(nums)
        # 获取列表长度
        l = len(num_sort)
        # result 列表用来保存三元组结果
        result=[]
        # 三元组中的第一位用i来表示,它之后还有两元,故最大值 l-3
        for i in range(l-2):
            # x 和 y 是双指针,一个略大于 i,一个在最大边界 l-1
            x,y = i+1,l-1
            # 若整个列表最小值大于0,则全部都大于0;若最大值小于0,则全为负
            if num_sort[i]>0 or num_sort[y]<0:
                # 以上情况不会出现和为 0 的三元组,直接返回空列表
                return result
            # 这里用来对第一元去重,如果新拿到的 i 值和上一轮 i 值相同,直接跳过本次循环后续内容
            if i>0 and num_sort[i]==num_sort[i-1]:
                continue
            # while 循环开启双指针的缩小范围
            while x < y:
                # 计算三元组的和
                temp = num_sort[i] + num_sort[x] + num_sort[y]
                # 如果和小于 0 ,为了得到和为 0 ,需要增加值,移动 x 指针
                if temp<0:
                    x += 1
                # 如果和大于 0,为了缩小到 0,需要减少值,移动 y 指针
                elif temp>0:
                    y -= 1
                # 如果和为0
                else:
                    # 记录三元组
                    lst = [num_sort[i],num_sort[x],num_sort[y]]
                    # 结果中添加三元组
                    result.append(lst)
                    # 检测 x 之后的值是否与 x 相同,若是,通过 while 循环提高 x 的值到最后一个重复值
                    while x<y and num_sort[x+1]==num_sort[x]:
                        x += 1
                    # 检测 y 之前的值是否与 y 相同,若是,通过 while 循环缩减 y 的值到第一个重复值
                    while x<y and num_sort[y-1]==num_sort[y]:
                        y -=1
                    # 此时统一对 x 和 y 移动寻找下一组三元组
                    x += 1
                    y -= 1 
        # 整个 for 循环完成后,将记录结果的列表返回       
        return result

提交答案

时间表现上出乎意料的好:

执行用时 : 656 ms, 在所有 Python3 提交中击败了 95.17% 的用户 内存消耗 : 16.4 MB, 在所有 Python3 提交中击败了 9.64%的用户

优化

上述代码是一步步尝试出来的,最后提到的通过三元去重来规避三元组重复的思路也是借鉴的题解,所以等到完成后,代码也基本和其余题解一致了。前前后后修改、提交了10次,第10次才勉强通过验证。包括很多优化的想法与代码也基本在代码中实现到了。

题目二

第 16 题 最接近的三数之和:

给定一个包括 n 个整数的数组 nums 和 一个目标值 target。找出 nums 中的三个整数,使得它们的和与 target 最接近。返回这三个数的和。假定每组输入只存在唯一答案。

代码语言:javascript
复制
例如,给定数组 nums = [-1,2,1,-4], 和 target = 1.

与 target 最接近的三个数的和为 2. (-1 + 2 + 1 = 2).

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

思路

如果顺利完成了第 15 题,对三数之和为特定值的情况熟悉了,到这个题目便可以省力许多。但我在这个题中还是采用比较保守,选用基于双指针优化过的穷举:仍是先将数组排序,遍历数组确定三个数的第一个,双指针代表剩余两个、分别位于范围两端。若判断三数之和小于 target,则向右移动较小的指针;若判断三数之和大于 target,则向左移动较大指针来缩小三数之和;若检测到三数之和为 target 则可提前结束直接返回 target 值了。

题目中只要求返回求和的值即可,但我仍是用字典保存了产生不同求和值情况下三个数的情况,具体我们来看代码。

代码

代码语言:javascript
复制
class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:
        # 刚用 sorted,这里用 sort 来进行排序
        nums.sort()
        l = len(nums)
        # dic 字典储存求和值对应的三数情况
        dic = {}
        # 三数中第一个用 i 来定位
        for i in range(l-2):
            # 双指针 x 和 y,左侧指针略大于 i,右侧指针在右侧边界
            x,y = i+1,l-1
            # while 循环来缩小双指针范围
            while x<y:
                # 三数求和
                temp = nums[i]+nums[x]+nums[y]
                # 将求和值和对应三个数组成的列表存储到字典中
                dic[temp] = [nums[i],nums[x],nums[y]]
                # 若和小于 target,为了更靠近,右移左侧指针增加和
                if temp < target:
                    x+=1
                # 若和大于 target,移动右侧指针
                elif temp >target:
                    y-=1
                # 若和为 target,提前结束返回
                else:
                    return target
        # 若执行完整个 for 循环,我们得到了双指针法求和结果的字典
        # 获取字典的 keys() 即求和值的列表,先排序
        target_key = sorted(list(dic.keys()))
        # 计算排序后第一位与 target 差值的绝对值
        target_min = abs(target - target_key[0])
        # result 用来记录返回结果
        result = 0
        # 对排序后的求和值列表进行遍历
        for i,n in enumerate(target_key):
            # 对每个求和值进行运算,求它们与 target 差值的绝对值
            tmp = abs(n-target)
            # 找最小的差值,即最接近 target
            target_min = min(target_min,tmp)
            # 如果此位置恰好满足更小的情况,将此时求和值赋值给 result
            if target_min == tmp:
                result = n
        # 经过遍历,把差值最小时的求和值已经赋值给 result了,最终返回            
        return result

提交答案

时间表现上依旧不错,应该是双指针法立功了:

执行用时 : 120 ms, 在所有 Python3 提交中击败了 71.18% 的用户 内存消耗 : 13.7 MB, 在所有 Python3 提交中击败了 9.38% 的用户

优化

回头看代码,感觉双指针法只是精简了遍历过程,我将所有的求和情况都记录在了字典中,最后再独立地对字典中的求和值进行运算找到与 target 最接近的值,这一步如果能优化下、通过双指针过程直接实现应该不错。

参考其它题解代码,确实如此,无需再单独对所有求和值进行新一轮比较,在求完和后直接比较保存即可,且题目只要求和值即可,无需我们定义的字典。

代码语言:javascript
复制
class Solution:
    def threeSumClosest(self, nums: List[int], target: int) -> int:        
        nums.sort()
        l = len(nums)
        # result 默认为无穷大
        result = float("inf")
        # 双指针法来表示三个数
        for i in range(l-2):
            x,y = i+1,l-1
            # 对 x 和 y 双指针进行处理
            while x<y:
                # temp 为此时三数的和
                temp = nums[i]+nums[x]+nums[y]
                if temp < target:
                    x+=1
                elif temp >target:
                    y-=1
                else:
                    return target
                # 如果此时和与 target 差值更小,则保存此求和到 result 中
                if abs(temp - target) < abs(result-target):
                    result = temp   
        return result

代码更精炼了,但貌似表现没有提高多少:

执行用时 : 136 ms, 在所有 Python3 提交中击败了 56.67% 的用户 内存消耗 : 13.6 MB, 在所有 Python3 提交中击败了 9.38% 的用户

猜测可能是这个计算差值绝对值的比较过程比较费时吧。

结论

第 15 和 16 题,两道均为中等难度题,解题思路都是基于双指针法进行精简过的穷举求和判断。也感谢这两道题,我对双指针法的理解又加深了些。

同时,最后一段优化代码中,接触到了 Python 中的无穷大 float("inf"),当然也顺手查到了无穷小 float("-inf"),之后如果有类似的比较最大值最小值,可以为参与比较的变量设定这么个初始值。

收获还行,明天继续~

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-23,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 TTTEED 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 题目一
    • 思路
      • 代码
        • 提交答案
          • 优化
          • 题目二
            • 思路
              • 代码
                • 提交答案
                  • 优化
                  • 结论
                  相关产品与服务
                  容器服务
                  腾讯云容器服务(Tencent Kubernetes Engine, TKE)基于原生 kubernetes 提供以容器为核心的、高度可扩展的高性能容器管理服务,覆盖 Serverless、边缘计算、分布式云等多种业务部署场景,业内首创单个集群兼容多种计算节点的容器资源管理模式。同时产品作为云原生 Finops 领先布道者,主导开源项目Crane,全面助力客户实现资源优化、成本控制。
                  领券
                  问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档