LeetCode 1 两数之和(Two Sum)

点此看全部题解 LeetCode必刷100题:一份来自面试官的算法地图(题解持续更新中)

生活中的算法

还记得上次去超市购物吗?你拿着一张100元钞票,挑选了一些商品,收银员告诉你总价是87元。这时候,收银员要找给你13元。但如果收银柜里只有零散的1元、5元、10元,收银员该如何快速地从这些零钱中找出两张,正好加起来等于13元呢?

这就是我们今天要讨论的"两数之和"问题在生活中的一个实例。

问题描述

LeetCode第1题"两数之和"是这样描述的:给定一个整数数组nums和一个整数目标值target,请你在该数组中找出和为目标值target的那两个整数,并返回它们的数组下标。

假设每种输入只会对应一个答案,而且不能重复使用相同的元素。

这不就是收银员找零钱的问题吗?数组就是收银柜里的零钱,target就是要找给顾客的金额。

最直观的解法:暴力穷举

如果你是收银员,最直观的做法是什么?大概率是这样:拿起第一张钞票,然后挨个和其他钞票配对,看看加起来是不是13元。不行的话,再拿第二张钞票,继续和后面的钞票配对…

这就是我们的暴力解法,它朴素但有效。具体来说:

  1. 拿起第一个数字
  2. 依次与后面的每个数字相加,检查是否等于目标值
  3. 如果找到了,就返回这两个数字的位置
  4. 如果没找到,就拿起第二个数字,重复步骤2和3
  5. 以此类推,直到找到答案或检查完所有可能

让我们用一个具体的例子来模拟这个过程:

  1. nums = [2, 7, 11, 15], target = 9
  2. 第一轮:拿起2
  3. 2 + 7 = 9,找到答案了!
  4. 返回[0, 1]
  5. 如果没这么幸运:
  6. 2 + 11 = 13,不是9,继续
  7. 2 + 15 = 17,不是9,继续
  8. 拿起7...

这种思路可以用Java代码这样实现:

  1. public int[] twoSum(int[] nums, int target) {
  2. // 外层循环:拿起第一个数
  3. for (int i = 0; i < nums.length; i++) {
  4. // 内层循环:依次与后面的数配对
  5. for (int j = i + 1; j < nums.length; j++) {
  6. // 如果两数之和等于目标值,返回它们的位置
  7. if (nums[i] + nums[j] == target) {
  8. return new int[]{i, j};
  9. }
  10. }
  11. }
  12. // 如果找不到答案,返回空数组
  13. return new int[]{};
  14. }

优化解法:哈希表

让我们回到超市的场景。假设收银员手里拿着一张5元,要找给顾客13元。这时候收银员在想:我现在需要找一张8元的(13 - 5 = 8)。如果能立即知道柜台里有没有8元,问题就解决了。

这就是哈希表解法的核心思想:我们可以使用一个哈希表来记录每个数字出现的位置,这样就能在O(1)时间内查找任何数字。

哈希表解法的原理

  1. 创建一个哈希表,用于存储每个数字及其下标
  2. 遍历数组中的每个数字current
  3. 计算需要配对的数字complement = target - current
  4. 在哈希表中查找complement
    • 如果找到了,说明我们找到了答案
    • 如果没找到,把当前数字和它的下标放入哈希表,继续遍历

算法步骤(伪代码)

  1. 创建空的哈希表map
  2. 对于数组中的每个数字nums[i]:
    • 计算complement = target - nums[i]
    • 如果map中包含complement,返回[map.get(complement), i]
    • 否则,将nums[i]和i放入map中
  3. 如果遍历完还没找到,返回空数组

示例运行

让我们用示例数据模拟一下:

  1. nums = [2, 7, 11, 15], target = 9
  2. 第一步:i = 0
  3. - current = 2
  4. - complement = 9 - 2 = 7
  5. - map为空,找不到7
  6. - 2和它的下标0放入map:{2:0}
  7. 第二步:i = 1
  8. - current = 7
  9. - complement = 9 - 7 = 2
  10. - map中找到了2
  11. - 返回[map.get(2), 1],也就是[0, 1]

Java代码实现

  1. public int[] twoSum(int[] nums, int target) {
  2. // 创建哈希表,用于存储数字和下标
  3. Map<Integer, Integer> map = new HashMap<>();
  4. // 遍历数组
  5. for (int i = 0; i < nums.length; i++) {
  6. // 计算需要的配对数字
  7. int complement = target - nums[i];
  8. // 查找配对数字是否存在
  9. if (map.containsKey(complement)) {
  10. // 找到答案,返回两个数字的下标
  11. return new int[]{map.get(complement), i};
  12. }
  13. // 将当前数字和下标放入哈希表
  14. map.put(nums[i], i);
  15. }
  16. // 如果找不到答案,返回空数组
  17. return new int[]{};
  18. }

暴力解法vs哈希表解法

让我们比较一下这两种解法:
暴力解法用两层循环检查所有可能的组合,时间复杂度是O(n²),但空间复杂度只有O(1)。它的优点是直观、易于理解,适合处理小规模数据。

哈希表解法只需要遍历一次数组,时间复杂度是O(n),但需要额外的哈希表存储数据,空间复杂度是O(n)。它用空间换时间,特别适合处理大规模数据。

题目模式总结

这道题虽然简单,但它体现了一个重要的算法模式:查找配对元素

这种模式在算法题中经常出现,比如:

  • 判断数组中是否存在两个数的差等于某个值
  • 在排序数组中找出两个数,它们的平方和等于某个值
  • 在字符串中找出两个字符,它们的ASCII码之和等于某个值

解决这类问题的通用思路是:

  1. 先思考暴力解法:两层循环遍历所有可能的组合
  2. 考虑优化:能否将"查找配对元素"的过程优化到O(1)时间复杂度
  3. 思考数据结构:通常哈希表是优化这类问题的利器

小结

通过这道题,我们不仅学会了如何解决"两数之和",更重要的是理解了一个常见的算法模式。下次遇到类似的"找配对元素"问题,我们就知道该如何思考了。

学习算法最重要的不是背解法,而是理解思维方式。希望这篇文章对你有帮助!


作者:忍者程序员
公众号:忍者算法

从购物找零到两数之和:一道经典算法题的深度解析|LeetCode 1 两数之和的更多相关文章

  1. [经典算法题]寻找数组中第K大的数的方法总结

    [经典算法题]寻找数组中第K大的数的方法总结 责任编辑:admin 日期:2012-11-26   字体:[大 中 小] 打印复制链接我要评论   今天看算法分析是,看到一个这样的问题,就是在一堆数据 ...

  2. 笔试算法题(20):寻找丑数 & 打印1到N位的所有的数

    出题:将只包含2,3,5的因子的数称为丑数(Ugly Number),要求找到前面1500个丑数: 分析: 解法1:依次判断从1开始的每一个整数,2,3,5是因子则整数必须可以被他们其中的一个整除,如 ...

  3. 笔试算法题(19):判断两条单向链表的公共节点 & 字符集删除函数

    出题:给定两个单向链表的头结点,判断其是否有公共节点并确定第一个公共节点的索引: 分析: 由于是单向链表,所以每个节点有且仅有一个后续节点,所以只可能是Y型交叉(每条链表中的某个节点同时指向一个公共节 ...

  4. NYOJ995硬币找零(简单dp)

    /* 题意:给你不同面额的硬币(每种硬币无限多),需要找零的面值是T,用这些硬币进行找零, 如果T恰好能被找零,输出最少需要的硬币的数目!否则请输出剩下钱数最少的找零方案中的最少硬币数! 思路:转换成 ...

  5. Newtonsoft.Json C# Json序列化和反序列化工具的使用、类型方法大全 C# 算法题系列(二) 各位相加、整数反转、回文数、罗马数字转整数 C# 算法题系列(一) 两数之和、无重复字符的最长子串 DateTime Tips c#发送邮件,可发送多个附件 MVC图片上传详解

    Newtonsoft.Json C# Json序列化和反序列化工具的使用.类型方法大全   Newtonsoft.Json Newtonsoft.Json 是.Net平台操作Json的工具,他的介绍就 ...

  6. Leetcode 001. 两数之和(扩展)

    1.题目要求 给定一个整数数组和一个目标值,找出数组中和为目标值的两个数. 你可以假设每个输入只对应一种答案,且同样的元素不能被重复利用. 示例: 2.解法一:暴力法(for*for,O(n*n)) ...

  7. 前端与算法 leetcode 1. 两数之和

    目录 # 前端与算法 leetcode 1. 两数之和 题目描述 概要 提示 解析 解法一:暴力法 解法二:HashMap法 算法 传入[1, 2], [11, 1, 2, 3, 2]的运行结果 执行 ...

  8. LeetCode:两数之和、三数之和、四数之和

    LeetCode:两数之和.三数之和.四数之和 多数之和问题,利用哈希集合减少时间复杂度以及多指针收缩窗口的巧妙解法 No.1 两数之和 给定一个整数数组 nums 和一个目标值 target,请你在 ...

  9. LeetCode 167.两数之和(C++)

    给定一个已按照升序排列 的有序数组,找到两个数使得它们相加之和等于目标数. 函数应该返回这两个下标值 index1 和 index2,其中 index1 必须小于 index2. 说明: 返回的下标值 ...

  10. leetCode:twoSum 两数之和 【JAVA实现】

    LeetCode 两数之和 给定一个整数数组,返回两个数字的索引,使它们相加到特定目标. 您可以假设每个输入只有一个解决方案,并且您可能不会两次使用相同的元素. 更多文章查看个人博客 个人博客地址:t ...

随机推荐

  1. CodeForces - 1336A Linova and Kingdom

    CodeForces - 1336A 就差一点点,很可惜,少发现个很显而易见的结论 就是一个点的价值,实际上就是(这个点的深度 - 之后的点的数目) 就是 \(depth_i - size_i\) 然 ...

  2. ScheduledThreadPoolExecutor与System#nanoTime

    一直流传着Timer使用的是绝对时间,ScheduledThreadPoolExecutor使用的是相对时间,那么ScheduledThreadPoolExecutor是如何实现相对时间的? 先看看S ...

  3. Spring源码学习 ------ IoC——AOP

    一直想抽空把Spring源码拿来读读,但真正去做这件事的时候发现不简单,Spring发展这么多年,它的规模已不是一个一般的开源框架所能比的,它的主要架构和流程不是非常清晰,很难抓到要害,但有一点可以肯 ...

  4. 安装cnpm时报错

    报错:npm WARN deprecated socks@1.1.10: If using 2.x branch, please upgrade to at least 2.1.6 to avoid ...

  5. golang之Time时间函数

    在编程中,我们经常会遭遇八小时时间差问题.这是由时区差异引起的,为了能更好地解决它们,我们需要理解几个时间定义标准. GMT(Greenwich Mean Time),格林威治平时.GMT 根据地球的 ...

  6. Educational Codeforces Round 155 (Rated for Div

    B. Chips on the Board 题解:贪心 显然我们可以把题意转化为:对于任意一个\((i,j)\),我们可以花费\(a_{i,j}\)的代价占据第\(i\)行和第\(j\)列,求占据所有 ...

  7. 使用IDEA一键发布应用

    1.编辑Dockerfile from java:8 WORKDIR /usr/local ADD ./target/jpaas-bpm.jar . CMD ["java",&qu ...

  8. zustand:基于hooks的react状态管理

    react的状态管理 状态(State)是 React 中用于存储组件数据的特殊对象,它可以影响组件的渲染输出.状态管理的核心目标是确保数据的一致性.可预测性以及组件之间的数据流. 每个 React ...

  9. LeetCode题集-6 - Z 字形变换

    题目:将一个给定字符串 s 根据给定的行数 numRows ,以从上往下.从左到右进行 Z 字形排列. 这一题作为中等难度,下面和大家分享几种不同的解法. 01.二维矩阵模拟法 所谓二维矩阵模拟法就是 ...

  10. MeteoInfo-Java解析与绘图教程(六)

    MeteoInfo-Java解析与绘图教程(六) 这一节主要说的是我们取到自动站的数据,如何通过插值,转化成格点数据,并绘制图层 //从数据库查询cimiss数据 List<Map<Str ...