【每日一题】【LeetCode】【第一天】三数之和

news2024/12/22 23:33:23

三数之和的解决之路= =

题干表述

在这里插入图片描述

测试案列(部分)

在这里插入图片描述
在这里插入图片描述

第一次思路

这种其实是最暴力的,也是我脑海里第一个想到的最简单的方法了。
思路就是三个循环,一个循环去一个数,然后当三个下标不同,且对应的三个数相加为0时就append追加进列表

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res_list = []
        index = range(len(nums))
        for i in index:
            for j in index:
                for k in index:
                    if i!=j and j!=k and i!=k and nums[i]+nums[j]+nums[k]==0:
                        res_list.append([nums[i], nums[j], nums[k]])
        return res_list

在这里插入图片描述

这种肯定会有错的,运行之后会发现,重复了很多排列顺序不同的三个同一元素。所以我们暂不考虑减少时间复杂度的情况,先考虑怎么解决去重的问题。

第二次

我们对于第一次代码中的三个!=进行优化,其实用小于号更好,既能避免下标重复,又能避免去重问题。

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res_list = []
        index = range(len(nums))
        for i in index:
            for j in index:
                for k in index:
                    if i<j and j<k and nums[i]+nums[j]+nums[k]==0:
                        res_list.append([nums[i], nums[j], nums[k]])
        
        return res_list

但结果还是不对,小于号虽然能解决一点去重问题,但是也就只能解决一点点。。
在这里插入图片描述

顺序不同的三个同一元素序列问题解决了,但是存在三个元素下标不同,但三个元素与另外三个元素重复的情况。
比如上图,两个[-1, 0, 1]其实是下标为0的-1和下标为4的-1造成的重复。

第三次

if判断的条件就得更严格,res_list里面有的列表,就不往里面添加。

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        res_list = []
        index = range(len(nums))
        for i in index:
            for j in index:
                for k in index:
                    if i<j and j<k and nums[i]+nums[j]+nums[k]==0:
                        if ([nums[i], nums[j], nums[k]] not in res_list) and ([nums[j], nums[i], nums[k]] not in res_list) and ([nums[j], nums[k], nums[i]] not in res_list) and ([nums[i], nums[k], nums[j]] not in res_list) and ([nums[k], nums[j], nums[i]] not in res_list) and ([nums[k], nums[i], nums[j]] not in res_list):
                            res_list.append([nums[i], nums[j], nums[k]])

        return res_list

这个测试是正确的,但是也是暴力解法,看起来太蠢了,哈哈哈,这么长的枚举判断我自己都受不了

在这里插入图片描述

但是提交会发现,超出时间限制。
执行到超时的案例是数值比较极端一个数据。

在这里插入图片描述

第四次

那肯定是那个枚举的if判断需要改善,因为想不到还有什么办法,翻了翻评论区,发现有人的代码第一步是将输入序列排序,自己立马想到,可能有序序列就不需要这种枚举,可能只需要一个[nums[i], nums[j], nums[k]] not in res_list即可

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res_list = []
        index = range(len(nums))
        for i in index:
            for j in index:
                for k in index:
                    if i<j and j<k and nums[i]+nums[j]+nums[k]==0:
                        if [nums[i], nums[j], nums[k]] not in res_list:
                            res_list.append([nums[i], nums[j], nums[k]])

        return res_list

排序之后,可以不用那么多if条件了,测试没问题,但提交还是超时了额

在这里插入图片描述

第五次

我想可能是三次循环太多了,这个if倒是可以往后放一放。
翻了翻评论区,注意到了一个哈希法

在这里插入图片描述

但后半部分提到的vector我只知道是向量,之前自己学Spark和数据结构的时候见过,但也只是知道它是向量而已,多的就不知道了。
但是前半部分的思路可以借鉴,通过0-a-b算出c,可以少一个循环。

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res_list = []
        n = len(nums)
        for i in range(n):
            for j in range(i+1, n):
                res = 0-nums[i]-nums[j]
                if res in nums[j+1:]:
                    if [nums[i], nums[j], res] not in res_list:
                        res_list.append([nums[i], nums[j], res])

        return res_list

改良了一下,改成了双循环,但是还是超时了??可能是range()比较耗时?不理解。。

在这里插入图片描述

第六次

参考了评论区的双指针法

在这里插入图片描述

有点像快排的每趟思路,首先要确立好基本思路,就是先排序为有序序列,然后每趟for循环从前到后选一个基准,然后对这个基准后面的序列进行两个元素的选取,使三个元素相加=0

然后这个选取两个元素的过程是,先取两头left(i+1的位置)和right(n-1的位置),然后sum大了,right-1sum小了left+1(因为是增序序列,所以这种逻辑是可以的,这里可能不常了解算法的人会烧CPU,可以反复理解一下,最好拿张纸写个序列试一试)。

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res_list = []
        n = len(nums)
        for i in range(n):
            left = i+1
            right = n-1
            while True:
                if left >= right:
                    break
                if nums[i]+nums[left]+nums[right] > 0:
                    right -= 1
                    continue
                elif nums[i]+nums[left]+nums[right] < 0:
                    left += 1
                    continue
                else:
                    if [nums[i], nums[left], nums[right]] not in res_list:
                        res_list.append([nums[i], nums[left], nums[right]])
                        break

        return res_list

按双指针思路写的代码,可惜,最后还是超时了。但最后的输入数据变了,是[0,0,0,0]没过。

在这里插入图片描述

第七次

对比了一下别人写的代码,修改了第七版的代码,发现了第六版还有些许错误,都在下面代码的注释里。

# -*- encoding: utf-8 -*-
class Solution(object):
    def threeSum(self, nums):
        """
        :type nums: List[int]
        :rtype: List[List[int]]
        """
        nums.sort()
        res_list = []
        n = len(nums)
        for i in range(n):
            left = i+1
            right = n-1
            # 增序序列第一个元素大于0,三数之和就不可能为0
            if nums[i] > 0:
                break
            # 没搞懂,暂先不加试试
            # if i >= 1 and nums[i] == nums[i-1]:
            #     continue
            while left < right:     # left<right这一限制条件就可以直接写到这来
                s = nums[i]+nums[left]+nums[right]      # 求和结果用到了两次,就可以先存储一个变量
                if s > 0:
                    right -= 1
                    # continue  加了等于没加,不需要continue
                elif s < 0:
                    left += 1
                    # continue  加了等于没加,不需要continue
                else:
                    if [nums[i], nums[left], nums[right]] not in res_list:
                        res_list.append([nums[i], nums[left], nums[right]])
                        # 这里不应该break,而是继续找下去,不然会漏
                        # break
                        left += 1
                        right -= 1

        return res_list

当然,代码里面还是有我的坚持。。。。原代码有两处没理解什么意思所以没加,先试了试我这一拼凑改良版。

在这里插入图片描述
这次连第一个测试案例都过不了,应该是死循环了。

第八次(抄的)

class Solution:
    def threeSum(self, nums):
        ans = []
        n = len(nums)
        nums.sort()
        for i in range(n):
            left = i + 1
            right = n - 1
            if nums[i] > 0:
                break
            if i >= 1 and nums[i] == nums[i - 1]:
                continue
            while left < right:
                total = nums[i] + nums[left] + nums[right]
                if total > 0:
                    right -= 1
                elif total < 0:
                    left += 1
                else:
                    ans.append([nums[i], nums[left], nums[right]])
                    while left != right and nums[left] == nums[left + 1]: left += 1
                    while left != right and nums[right] == nums[right - 1]: right -= 1
                    left += 1
                    right -= 1
        return ans

和自己一开始到最后的思路不同的是以下几个点:

  1. 能减少循环次数的就要减少。
    比如循环一开始的这个语句
if nums[i] > 0:
    break

升序序列第一个元素就比0大,那三个数的和就肯定>=0了,就不需要进行后续的while循环了。

  1. 去重的方法太费时间了,nums很长的时候if [nums[i], nums[left], nums[right]] not in res_list也得要O(n),这个点最关键,体现的地方有两处代码。
# 第一处(循环一开始处的语句)
if i >= 1 and nums[i] == nums[i - 1]:
    continue

# 第二处(while循环中else分支的语句)
while left != right and nums[left] == nums[left + 1]: left += 1
while left != right and nums[right] == nums[right - 1]: right -= 1

第一处是如何巧妙去重的呢,自己单看第一遍的时候没有理解,在手动模拟了一遍流程渐渐理解了。

整体流程(只手写了三轮循环):

在这里插入图片描述
过程描述:

序列[-1, 0, 1, 2, -1, -4]进入函数,首先会被升序排序变为[-4, -1, -1, 0, 1, 2],然后开始循环。

【第一轮for循环】i指向下标为0的地方(也就是-4),按规矩left就指向下标为0+1=1的地方(也就是-1),right指向下标为6-1=5的地方(也就是2
此时total=-4+(-1)+2=-3<0,所以进行left+1操作。
然后可以看出,经过四次left+1操作,因left=right,导致while循环结束,第一轮for循环结束。

此轮循环中,left指针移动4次、right指针移动0次、比较次数应为4次。

【第二轮for循环】i指向下标为1的地方(也就是-1),按规矩left就指向下标为1+1=2的地方(也就是-1),right指向下标为6-1=5的地方(也就是2
此时total=-1+(-1)+2=0,所以进行append()操作(添加了[-1, -1, 2]),向结果列表res中追加此时的三元组。但是,while循环并没有结束!(之前自己算法设计的思路就break结束了,会导致漏掉的情况)
此时同时进行left+1right-1操作,然后继续寻找是否存在更多的合法三元组。然后又找到了[-1, 0, 1],继续append(),然后继续对leftright进行操作,导致while循环条件不成立,while循环结束,for循环结束。

此轮循环中,left指针移动2次,right指针移动了2次,比较次数应为2次。

【第三轮for循环】i指向下标为2的地方(也就是-1),此时判断nums[i] == nums[i - 1],就会跳过while循环,直接开始第四轮for循环。
但是,为什么这样就跳过了呢?这样就能避免重复?先拿这个栗子说明。
下标为1的-1可以在下标从2到5的4个元素中有两组数据符合要求,那么下标为2的-1在下标从3到5的3个元素中,只有两个可能,一个是有符合要求的数据,一个是没有。有符合要求的数据,那么==一定是重复的==,可以跳过!没有,那也就不用往结果列表里面追加,所以可以跳过。
所以,nums[i] == nums[i - 1]的条件下,就是可以直接跳过,不用费时间。

至于第二处的代码,也是同理。只能说巧妙。。。知识upup

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/135519.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

FPGA设计CPU书籍

一直以来CPU内部是绝大多数IT工程师难以触及的领域。纵使学习过计算机架构相关课程&#xff0c;自己动手实现CPU也始终遥不可及&#xff0c;因为这涉及计算机系统的最底层——芯片设计。 而近年来FPGA芯片产品的发展与普及打破了这一阻碍&#xff0c;利用内部电路可重编程的FPG…

【C++进阶】IO流

&#x1f387;C学习历程&#xff1a;入门 博客主页&#xff1a;一起去看日落吗持续分享博主的C学习历程博主的能力有限&#xff0c;出现错误希望大家不吝赐教分享给大家一句我很喜欢的话&#xff1a; 也许你现在做的事情&#xff0c;暂时看不到成果&#xff0c;但不要忘记&…

Docker进阶(中)

docker 进阶&#xff08;中&#xff09;docker提交镜像等命令docker 镜像原理docker 私有库&推送到私有库容器数据卷docker 安装常规软件docker提交镜像等命令 再这个谈这个docker 提交这个镜像之前我们先补充一下上一篇博客没有谈到的命令。再这里说一下。我们之前谈到的…

代码随想录算法训练营第六天 java :242.有效的字母异位词 349. 两个数组的交集 ,1. 两数之和

文章目录哈希表理论基础哈希碰撞&#xff1a; 拉链法和线性探测法线性探测法Leetcode242.有效的字母异位词题目链接思路AC代码Leetcode349. 两个数组的交集题目链接思路AC代码Leetcode 1. 两数之和题目链接思路与难点AC代码收获今日收获哈希表理论基础 哈希函数如下图所示&…

【C++】string (上)(string类的常用接口 string类对象的容量操作 string类对象的访问及遍历操作 string类对象的修改操作)

文章目录string标准库中的string类string类的常用接口string类对象的容量操作string类对象的访问及遍历操作string类对象的修改操作string string是一个专门管理字符数组的类。 标准库中的string类 string是表示字符串的字符串类该类的接口与常规容器的接口基本相同&#xff0…

计算机二级python考前复习笔记

Python是一种解释型、面向对象、动态数据类型的高级程序设计语言程序设计风格&#xff1a;清晰第一&#xff0c;效率第二。结构化程序设计原则&#xff1a;自顶向下&#xff0c;逐步求精&#xff0c;模块化&#xff0c;限制使用goto语句&#xff08;Python无 goto 语句&#xf…

【回答问题】ChatGPT上线了!SLAM有哪些模型实现代码/案例/github源码?推荐10个以上比较好的SLAM深度学习模型?

目录SLAM有哪些模型实现代码&#xff1f;SLAM有哪些模型实现案例&#xff1f;SLAM有哪些模型的github源码&#xff1f;推荐10个以上比较好的SLAM深度学习模型&#xff1f;推荐10个以上比较好的SLAM深度学习模型github源码&#xff1f;SLAM有哪些模型实现代码&#xff1f; SLAM…

阿里云云数据库RDS的基本使用(二十三)

文章目录1.查看RDS数据库的基本信息2.查看RDS数据库的连接地址3.创建数据库账号并配置白名单3.1.创建数据库连接账号3.2.将ECS服务器添加到RDS白名单3.3.在ECS中登陆RDS数据库4.查看RDS数据库的监控5.查看RDS服务可用性6.查看RDS数据库的日志在RDS实例列表中点击管理即可跳转到…

ubuntu20驱动双屏问题总结

一、环境 设备&#xff1a;拯救者R7000P 显卡&#xff1a;NVIDA GeForce RTX 2060 系统&#xff1a;windows10ubuntu20的双系统下 显示器&#xff1a;笔记本显示器arzopa便携式显示器&#xff08;使用的type-c接口&#xff09; 驱动&#xff1a;nvidia-driver-520 二、问题…

【GO】K8s 管理系统项目[API部分--Service]

K8s 管理系统项目[API部分–Service] 1. 接口实现 service/dataselector.go // service type serviceCell corev1.Servicefunc(s serviceCell) GetCreation() time.Time {return s.CreationTimestamp.Time }func(s serviceCell) GetName() string {return s.Name }2. servic…

【C++】-- 海量数据处理

目录 位图 位图概念的引入 位图的实现 实现功能 开辟bit空间 数据输入set 数据删除reset 数据确认test 代码汇总 容器位图的衍生使用 布隆过滤器 布隆过滤器提出 布隆过滤器概念 ​布隆过滤器的实现 布隆过滤器的删除 布隆过滤器的特点 ​布隆过滤器的误判率 …

【电动车】基于削峰填谷的电动汽车多目标优化调度策略研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

计算机组成原理复习:计算机系统概述

1. 计算机系统概述 1.1 计算机系统的层次结构 &#xff08;1&#xff09; 硬件上&#xff0c;计算机系统可以分为五大功能部件&#xff1a; 运算器、控制器、存储器、输入设备、输出设备 将围绕其工作原理、逻辑实现、设计方法以及相互连接构成整机的方法展开 在典型的冯诺…

Android metaRTC6.0 编译指南

概述 metaRTC新版本优化了安卓系统支持&#xff0c;demo将C和C生成lib库&#xff0c;在lib库上提供了纯Java的webRTC推拉流demo。 demo支持软硬编解码&#xff0c;软编码为openh264&#xff0c;软解码为yangh264decoder&#xff0c;gpu编解码为mediacodec。 metaRTC android…

全长扩增子医学版新增内容来啦(一)

随着全长扩增子报告内容的扩充&#xff0c;医学版报告单的呼声越来越高&#xff0c;今天就给大家介绍一下凌恩生物针对医学客户&#xff0c;变更/新增了哪些报告内容~ 首先我们来看一下变更的内容吧&#xff01; CCA/RDA分析、PICRUSt2功能预测、随机森林-biomarker鉴定、随机…

多线程之Thread类常见方法及线程的状态

Thread类常见方法&#xff1a; 创建、启动线程、终止线程、线程等待、获取线程实例、线程休眠 1.关于如何创建线程&#xff0c;在专栏的上一篇文章已经谈到了有5中创建线程的方式。 2.启动线程&#xff08;t.start&#xff09;&#xff1a;调用 start 方法, 才在操作系统的底…

ArcGIS基础实验操作100例--实验39编辑公共边与顶点

本实验专栏参考自汤国安教授《地理信息系统基础实验操作100例》一书 实验平台&#xff1a;ArcGIS 10.6 实验数据&#xff1a;请访问实验1&#xff08;传送门&#xff09; 高级编辑篇--实验39 编辑公共边与顶点 目录 一、实验背景 二、实验数据 三、实验步骤 &#xff08;1&…

《操作系统-真象还原》14. 文件系统

文章目录文件系统概念inode目录项与目录通过文件名查找文件的流程超级块与文件系统布局文件控制块 —— FCB创建文件系统创建相关结构创建文件系统挂载分区文件描述符文件描述符和文件表文件描述符文件表文件描述符与 inode 关联关系文件描述符的实现文件操作相关的基础函数ino…

单例模式

单例模式 1. 单例模式介绍 单例模式可以说是整个设计中最简单的模式之一&#xff0c;而且这种方式即使在没有看设计模式相关资料也会常用在编码开发中。 因为在编程开发中经常会遇到这样一种场景&#xff0c;那就是需要保证一个类只有一个实例哪怕多线程同时访问&#xff0c;…

C#语言实例源码系列-实现IC卡的读写

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…