【LeetCode】修炼之路-0001-Two Sum(两数之和)【python】【简单】

news2024/11/26 14:51:01

前言

 计算机科学作为一门实践性极强的学科,代码能力的培养尤为重要。当前网络上有非常多优秀的前辈分享了LeetCode的最佳算法题解,这对于我们这些初学者来说提供了莫大的帮助,但对于我这种缺乏编程直觉的学习者而言,这往往难以消化吸收。(为什么别人就能想出这么优雅,高级的实现!我就只会暴力呢)我浅薄地认为,只有理解算法设计的思路,才能真正掌握编程技巧。鉴于此,本系列试图呈现另一种LeetCode修炼之路——从基本原理出发,一步步拓展思路,逐级深化难度。我将带您逐步经历算法设计的整个思考过程,不仅呈现最终成果,更重要的是共同经历这场求知的长征!
 笔者才疏学浅,难免疏漏错误。衷心希望聪明的您可以批评指正,大胆留下您的评论,让我们不断完善这套题解体系。

题目

Given an array of integers numsand an integer target, return indices of the two numbers such that they add up to target.

You may assume that each input would have exactly one solution, and you may not use the same element twice.

You can return the answer in any order.

Example 1:

Input: nums = [2,7,11,15], target = 9
Output: [0,1]
Explanation: Because nums[0] + nums[1] == 9, we return [0, 1].

Example 2:

Input: nums = [3,2,4], target = 6
Output: [1,2]

Example 3:

Input: nums = [3,3], target = 6
Output: [0,1]

题目译文

给定一个整数数组nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。

你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。

你可以按任意顺序返回答案。

知识点

  • 知识点1:range()函数的使用

Python 中的 range() 函数用于生成一系列数字。它通常用于 for 循环中,以便遍历一系列数字。这个函数可以接受一个、两个或三个参数:
Start(开始):序列的起始值。这个参数是可选的,如果不填,默认从 0 开始。
Stop(结束):序列的结束值。range 生成的序列会一直到这个数,但不包括这个数。这个参数是必须的。
Step(步长):序列中每个数字之间的差值。这个参数是可选的,如果不填,默认步长为 1。
下面是一些使用 range() 的例子:
range(5) 生成从 0 到 4 的序列(即 [0, 1, 2, 3, 4])。
range(1, 5) 生成从 1 到 4 的序列(即 [1, 2, 3, 4])。
range(0, 10, 2) 生成从 0 到 8 的偶数序列(即 [0, 2, 4, 6, 8])。
range() 函数生成数字是惰性的,这意味着它按需一次生成一个数字,而不是一次性生成所有数字。这在处理大范围时非常有益,因为它有助于节省内存。

  • 知识点2:嵌套循环的写法

嵌套循环常用于处理多维数据,如二维表格、多维数组等。外层循环控制行,内层循环控制列。还可以用于全排列、组合等场景。
嵌套循环可以多层嵌套,实现多重控制。需要注意循环变量不要重名,内层循环会影响外层循环变量。
在示例代码中 程序先执行外层循环,将外层变量i从0迭代到9。在每轮外层循环中,内层循环会重新从0开始执行,将内层变量j迭代到9。内层循环完整地执行完毕后,外层循环才会进行下一轮迭代。

for i in range(10): # 外层循环
    for j in range(10): # 内层循环
        print(f"{i}{j}") #语句体
  • 知识点3:如何快速判断一个元素是否出现在数组中

 哈希表可以快速实现判断一个元素是否出现在数组中。 通过直接访问存储位置、避免顺序比较、分布均匀等特性,哈希表实现了 O(1) 的优秀查找时间复杂度。遍历数组时,需要快速查找目标值减去当前元素的值是否存在,哈希表的查找时间复杂度为O(1),可以快速判断。

  • 知识点4:哪些语言原生支持哈希表

 哈希表是一种基础的数据结构,你能想到的编程语言基本都原生支持哈希表:

Python:字典(dict)类型就是基于哈希表实现的

 Java: HashMap和HashSet等基于哈希的集合都是原生支持的。

 C++:STL库提供了unordered_map和unordered_set作为哈希表的实现。

 C#: Dictionary 和 HashSet 类型原生支持哈希表。

 JavaScript:对象可以看作哈希表,ES6新增了Map数据结构。

 Go: Go语言通过map和set内置地支持了哈希表。

 Ruby:Hash类提供了字典实现,基于哈希表。

 Swift:字典(Dictionary)类型基于哈希表。

 PHP:关联数组(associative array)依赖哈希表实现。

 Rust: std::collections提供了HashMap和HashSet结构。

 Scala:原生支持HashMap和HashSet类。

思路分析(入门)

初出茅庐

首先我们要计算两个数的和是否等于target,那么我们肯定需要两个变量。var1var2
大家肯定不难想到最直接的实现,令var1=target[i],即target里的第i个元素。var2从下一个元素开始,通过全排列组合出所有可能,然后最后测试是否满足target

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        for i in range(len(nums)):
            for j in range(i+1, len(nums)):
                if nums[i] + nums[j] == target:
                    return [i, j]

在这里插入图片描述
  这种方法被称"暴力破解“(Brute Force),这是一种通过尝试所有可能性来获取问题解决方案的方法,它一定能通过,但是成本极高,通过暴力实现的算法,可以认为没有进行程序设计。让我们看看我们的算法有什么问题,我们如何来改进。
我们现在的实现是,通过全排列列出了所有的可能,并一个一个尝试,直到通过。
  想象一下,我们的实现就好比是,我们在一场冒险里,就是这个沉默的巨像接收你给他的两个下标,他再来判断是否通过这场考验。我们像无头苍蝇一样,一直尝试,直到通过。"暴力破解"一般遗漏了题目里隐含的信息,所以导致我们的算法实现是低效的。
在这里插入图片描述
(powered by DALL-E-3)

筑基初期

回头看一下题目,我们是知道两数和的,也就是说我拿到第一个数后,其实我就知道我要找的第二个数是什么了,因为使等式target成立的另一个解即var2=target-var1,那么现在我们要做的,就是如何快速如何快速判断var2是否出现在数组中,啊哈!没错!使用哈希表,

python里基于hash的实现有set()dict{},set更多的能判断某个值是否存在这个集合,而dict可以实现key-value两个值都可以获取,这里我们要解决的问题是返回的是下标,而不是整数数组nums 是否存在 target解的结论。因此我们的数据结构要使用dict,key用来存储数组里的值,value用来存储下标。如果题目要求为只返回是否存在解,我们会考虑使用set()

那么我们需要执行这个操作,先构建这个hashmap,这样我们就能随心所欲的查询var2了!让我们来构建如下。

# 定义twoSum函数,输入为数组nums和目标值target,这是leetcode系统为你写好的
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
	  # 创建空字典,用于存储数组元素值到索引的映射
	    hashmap = {}
	
	  # 遍历数组,将每个元素值与索引存入字典
	    for i in range(len(nums)):
	        hashmap[nums[i]] = i

然后我们有了 一个强大的哈希表,它存储了所有元素还有下标,我们遍历它岂不是更好,我们先通过对数组进行“编码”(这里抽象为对信息或者特征的提取)得到了哈希表,然后基于这个哈希表完成我们的任务。太好了,感觉思维也很干净(隐隐闪过一丝不安),我们实现代码如下:

先用你聪明的脑袋想想,这样的实现有没有逻辑问题,思考一下!

# 定义twoSum函数,输入为数组nums和目标值target
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
	  # 创建空字典,用于存储数组元素值到索引的映射
	    hashmap = {}
	
	  # 遍历数组,将每个元素值与索引存入字典
	    for i in range(len(nums)):
	        hashmap[nums[i]] = i
	
	    # 遍历字典  
	    for key in hashmap:
	      
	    # 计算当前键对应元素的补数 
	        var2 = target - key
	
	    # 如果var2存在于字典中,且不是key本身
	        if var2 in hashmap and hashmap[var2] != hashmap[key]:  # 题目要求不能使用同一元素两次,所有有这个and的限制条件
	      
	         # 返回当前键值对应的索引,和var2对应的索引
	            return [hashmap[key], hashmap[var2]]

折戟沉沙

提交!嗯?错误答案,分析分析
在这里插入图片描述
对于[3,3]测试用例,我们返回的是空,找不到,因为我们第二次遍历的是哈希表,而不是原始数组,通过“编码”得到的信息回导致失真,它的数据密度更高,但是会丢掉一些信息,自己要注意进行数据处理时,会导致的相关影响,例如我们在把数组转换成哈希表的时候,实际上会进行去重,[3,3]的数组转换成哈希表后,只留下了{3:1}这个结果,第二个3的下标会覆盖第一个,所以我们进行哈希表查询就返回为空了。

风云再起

好的,让我们再接再厉,这次我们遍历我们的数组,然后去哈希表里去找答案试试

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
	  # 创建空字典,用于存储数组元素值到索引的映射
	    hashmap = {}
	
	  # 遍历数组,将每个元素值与索引存入字典
	    for i in range(len(nums)):
	        hashmap[nums[i]] = i
	
	    # 遍历数组 
	    for i in range(len(nums)):
	      
	    # 计算当前键对应元素的var2
	        var2= target - nums[i]
	
	    # 如果var2存在于字典中,且不是key本身
	        if var2 in hashmap and hashmap[var2] != i:
	      
	         # 返回当前键值对应的索引,和var2对应的索引
	            return [i, hashmap[var2]]

提交!nice,总算是accepted了
在这里插入图片描述

雄霸天下

看我们提交的结果,只击败了41.92%,啧啧啧,这可不行,再看看我们的代码有没有优化的地方,
在这里插入图片描述在这里插入图片描述
可恶!同样的代码,我们竟然写了两次,今天我们学习一个咒语(加州口音让你更强大):Don’t Repeat Yourself!
DRY (Don’t Repeat Yourself) 原则:
原则的核心思想包括:

  • 避免代码重复 - 对于相同或类似的代码,抽象成函数、模块等进行重用
  • 将变化点抽象成参数、配置等,使代码更灵活
  • 优先使用组合而非继承来重用代码
  • 对所有重复实现进行提取、抽象、封装
  • 重复的代码往往表示设计需要优化

想象一下在我们这里抽象成函数的话,只是换汤不换药,还是走了两次遍历,我们这里需要的是优化,比如我们看一下两次循环做的事情,第一次遍历数组建立hash表,第二次遍历数组找哈希表,我们能不能把这两次业务操作合并在一次循环里呢,流程改成遍历数组,拿到var1,计算var2的值,查找var2,如果存在则返回下标,如果不存在则添加到字典里,完美!让我们来试试这个全球最强代码,完美,优雅,除了变量命名和没有使用enumerate(),其他都是满分!

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
	  # 创建空字典,用于存储数组元素值到索引的映射
	    hashmap = {}
	    
	  # 遍历数组,将每个元素值与索引存入字典
	    for i in range(len(nums)):

	    # 计算当前键对应元素的var2
	        var2= target - nums[i]
	
	    # 如果var2存在于字典中,且不是key本身
	        if var2 in hashmap and hashmap[var2] != i:
	         # 返回当前键值对应的索引,和var2对应的索引
	            return [i, hashmap[var2]]
	        hashmap[nums[i]] = i

试试!
在这里插入图片描述
在这里插入图片描述
**41.66%???!**这咋还往下掉了呢,我倒要看看,是谁的代码超过了我们。

在这里插入图片描述
可以看到我们排名第二,什么!竟然还有比我们更强的存在,让老夫看看。
在这里插入图片描述
啊?竟然通过创建一个文件输出流f,指向user.out文件,然后使用print语句输出所有结果答案到该文件中,然后头也不回地调用exit(0)退出程序,这样当代码被运行测试的时候,它会直接把事先准备好的答案打印到输出文件中,从而通过测试。宛如学霸上场考数学B卷,直接写满B卷答案的同时,还把A卷和C卷的答案也写上了,裁判直呼卧槽!满分,必须满分!但是同学们,咱们不要学他噢,思维还是按我们的来,咱们的实现才是全球地表最强!这葵花宝典,不练也罢。【这段准确性有误,关于这个hack实现是如何通过验证的,还请网友批评指正】

# 逆练心法的最优解,因为leetcode会增加测试用例,所以现在leetcode里提供的这个性能最佳的代码已经无法通过测试了。
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        f = open('user.out', 'w')
        print('''[1, 0]
        [2, 1]
        [1, 0]
        [2, 0]
        [2, 1]
        [3, 0]
        [2, 0]
        [4, 2]
        [2, 1]
        [1, 0]
        [3, 2]
        [2, 1]
        [2, 0]
        [4, 0]
        [1, 0]
        [3, 2]
        [4, 2]
        [5, 2]
        [3, 0]
        [4, 3]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [1, 0]
        [4, 0]
        [11, 5]
        [1, 0]
        [9999, 9998]
        [6,8]
        [6,9]
        [12,25]
        [16,17]''',file=f)
        exit(0)

在这里插入图片描述

代码纯享版

class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        hashmap = {}
        for i, num in enumerate(nums):
            if target - num in hashmap and hashmap[target - num] != i:
                return [i, hashmap[target - num]]
            hashmap[num] = i

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

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

相关文章

tcp/ip实现两个手机之间连接同步显示

app主界面 选择一:TCP客户端 选择二:TCP服务端 点击下图item时进入曲线绘制页面 如果是服务器端它不需要连任何设备就可以直接进入绘制界面如果是TCP的话就不能直接进入,否则就会提示未连接网络连接不能放在主线程,页面去调方法&…

Java限流方案常用算法详解 固定时间窗口 滑动时间窗口 漏桶限流 令牌桶限流

前言 为什么要做限流? 服务需要保护自己,以免被太多的请求淹没(无论是恶意或无意的),从而保持可用性。 举个生活中的例子,某个景区,平时可能根本没什么人前往,但是一旦到了国庆假日…

Python中的并发编程(7)异步编程

异步编程 Python3.4后新增了asyncio模块,支持异步编程。 异步是在一个线程中通过任务切换的方式让多个任务”同时“进展。asyncio不涉及线程/进程切换,减少了线程/进程创建、上下文切换的开销,更轻量级。 asyncio的核心是事件循环&#xff0…

仓储3代电子标签接口文档-V1.2

电子标签标签注册 通过手动触发电子标签注册到系统,注册成功就可以进行功能测试。 仓储3代注册 方式1:(四灯外供电版本标签) 标签左测中间按键连按三次 方式2:(电池供电版本标签) 标签右下角左下角按键&#xff0…

开放网络+私有云=?星融元的私有云承载网络解决方案实例

在全世界范围内的云服务市场上,开放网络一直是一个备受关注的话题。相比于传统供应商的网络设备,开放网络具备软硬件解耦、云原生、可选组件丰富等优势,对云服务商和超大型企业有足够的吸引力。 SONiC作为开源的网络操作系统,使得…

SV接口的驱动和采样_2023.12.27】

cb 使用cloking block进行信号的同步 在cloking block,所有信号的采样和驱动,都是和时钟同步的 clocking cb (posedge clk); input grant; output request; endclocking接口同步 用和wait来同步测试平台中的信号 bus.cb; 接口…

QT UI自动化测试(1)

一、框架选择 想结合公司产品搭建一套自动化测试框架,一方面自己学习用,一方面也希望跟公司业务结合起来,双赢。公司软件最多的产品是部署在Linux系统上,基于QT QML开发的UI,本来奔着免费的自动化框架去的,…

PulseGAN

研究背景 远程光电容积描记术 (rPPG) 是一种非接触式技术,用于测量面部视频中的心脏信号。健康监测和情绪识别等许多领域都迫切需要高质量的 rPPG 脉冲信号。然而,由于脉搏信号不准确的限制,现有的大多数rPPG方法只能用于获取平均心率&#…

Selenium自动化教程03:延时等待的3种方式

我们经常会碰到用selenium操作页面上某个元素的时候,需要等待页面加载完成后,才能操作。否则页面上的元素不存在,会抛出异常。或者碰到AJAX异步加载,我们需要等待元素加载完成后,才能操作。在进行UI自动化测试时&#…

骑砍MOD天芒传奇-任务列表

一.真假仁宗 进入场景后找到假的仁宗并击杀,只能问一个问题.但你不知道他是否是说真话的那个人! dlga_rz_question_list:question1|那 个 是 仁 宗 ? dlga_rz_question_list:question2|你 是 个 说 真 话 的 人 吗 ? dlga_rz_question_lis…

【Linux】虚拟内存

文章目录 一、 介绍二、虚拟内存改配置多大?三、配置步骤 一、 介绍 虚拟内存(也称为交换空间),是一种计算机操作系统的内存管理技术,它通过将部分存储器空间用作硬盘上的临时扩展,使得程序能够使用比实际…

.NetCore NPOI 读取excel内容及单元格内图片

由于数据方提供的数据在excel文件中不止有文字内容还包含图片信息,于是编写相关测试代码,读取excel文件内容及图片信息. 本文使用的是 NPOI-2.6.2 版本,此版本持.Net4.7.2;.NetStandard2.0;.NetStandard2.1;.Net6.0。 测试文档内容&#xf…

基于Spring Cloud + Spring Boot的企业电子招标采购系统源码

随着企业的快速发展,招采管理逐渐成为企业运营中的重要环节。为了满足公司对内部招采管理提升的要求,建立一个公平、公开、公正的采购环境至关重要。在这个背景下,我们开发了一款电子招标采购软件,以最大限度地控制采购成本&#…

SSM驾校预约管理系统----计算机毕业设计

项目介绍 本项目分为管理员、教练、学员三种角色, 管理员角色包含以下功能: 学员管理、教练管理、车辆管理、关系管理、车辆维修管理、个人中心等功能。 教练角色包含以下功能: 我的课程、我的学员、车辆中心、个人中心等功能。 学员角色包…

软件测试/测试开发丨Python、pycharm 安装与环境配置

Python 安装与环境配置 1. Python 安装 版本推荐 3.10.0下载地址:www.python.org/downloads/w… 若需要安装旧版本,在页面下方选择对应版本即可,MacOS选择对应系统即可 图示下载windows 3.11.4版本 安装Python 执行安装程序,安…

FreeRTOS学习--53讲 任务通知

任务通知定义 a.任务通知像个用于通知的任务,由一个32位无符号整数和8位的通知状态组成,通过任务通知函数改写其他任务的32位无符号整数数据。 b.改写方式如下(1.可以让这个整数加1: 模拟信号量 2. 设置该整数的指定的某些位:模拟事件组 3.直…

C++ 类和对象 (上)

类的引入: 由于C语言是面向过程的编程语言,我们在完成一件事的时候通常习惯将一件事拆分成一个一个小过程来实现,而到了C就习惯将一件事分成不同的模块,交给不同的对象来处理,每一个对象中承载着数据类型和函数。 &am…

网格布局(大练习)

最近对网格布局研究了一下,写了一个简单的demo。可以参考参考~ 网格基础布局:github地址 挤占网格布局:github地址 基础网站格局:github地址 复杂网站格局(方式一):github地址 复杂网站格局&am…

MySQL的高级SQL语句

目录 1.mysql高阶查询 select:显示表格中一个或数个字段的所有数据记录 distinct:不显示重复的数据记录 where:有条件查询 AND OR :且 或 in:显示已知值的数据记录 between:显示两个值范围内的数据记…

深入ArkUI:深入实战组件text和text input

文章目录 Text组件介绍Text组件的属性方法Text:文本显示组件4.3TextInput组件实战案例:图片宽度控制页面本文总结要点回顾在今天的课程中,我们将深入学习ArkUI提供的基础组件,着重探讨text和text input两个组件。 Text组件介绍 Text组件是一个用于显示文本的组件,其主要作…