两数之和(leetcode)

两数之和 (LeetCode #1)

题目

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

你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。

示例:

复制代码
输入:nums = [2, 7, 11, 15], target = 9
输出:[0, 1]
解释:因为 nums[0] + nums[1] == 9,返回 [0, 1]

解法一:最常规的思路

拿到这道题,直觉反应是:把所有可能的两个数组合都试一遍 。对于每个位置 i,看看后面每个位置 j,检查 nums[i] + nums[j] 是否等于 target

换句话说,就是两层循环暴力枚举。

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

时间复杂度:O(n²)空间复杂度:O(1)

思路有了,逻辑也对。但这个做法有没有可以省掉的地方?来看一个最坏情况------

假设 nums = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]target = 19(答案是最后两个数 9 和 10,下标 8 和 9)。暴力法的执行过程:

i j 的范围 检查次数 这些检查里,有多少是重复的?
0 1~9 9 次 i=1 时还要再检查 2~9
1 2~9 8 次 i=2 时还要再检查 3~9
2 3~9 7 次 ...
... ... ... 每一轮都在重复前面的工作
7 8~9 2 次 最后才找到答案
8 9 1 次 找到答案

总检查次数:9 + 8 + 7 + 6 + 5 + 4 + 3 + 2 + 1 = 45 次

对于长度为 n 的数组,最坏情况下要检查 n(n-1)/2 个数对。

如果数组有一万个元素,暴力法要检查约 50,000,000 次

问题就出在这里:每次都在重新找配对,却从不记住已经见过的数。


解法二:哈希表 ------ 换一种问法

那能不能换一种问法,让查找变成 O(1)?

与其问:nums[i] + nums[j] == target ?

不如问:target - nums[i] 这个数,我之前见过吗?

这个转变彻底改变了问题的性质:

  • 原来要遍历后面所有数来"找配对",O(n)
  • 现在只需要查一下哈希表,O(1) 就知道答案

按这个思路来写,会变成这样------

python 复制代码
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        mem = dict()
        for idx, num in enumerate(nums):
            if target - num in mem:
                return [mem[target - num], idx]
            mem[num] = idx
        return [-1, -1]

这段代码已经达到最优了。来看看关键设计------

"先查再存"的顺序

  1. 先看 target - num 是否在表中(找配对)
  2. 再把当前数存入表中(留给后面的数来配对)

这样做天然避免了"用同一个元素两次"的问题,因为当前数还没存进去。

也可以这样写,把 target - num 提取出来更清晰:

python 复制代码
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        mem = dict()
        for idx, num in enumerate(nums):
            complement = target - num
            if complement in mem:
                return [mem[complement], idx]
            mem[num] = idx
        return [-1, -1]

时间复杂度:O(n)空间复杂度:O(n)


解法三:双指针(排序后)

如果数组已经排序,或者允许排序,还有一种思路也值得知道------

python 复制代码
class Solution:
    def twoSum(self, nums: List[int], target: int) -> List[int]:
        sorted_nums = sorted(enumerate(nums), key=lambda x: x[1])
        left, right = 0, len(sorted_nums) - 1
        
        while left < right:
            current_sum = sorted_nums[left][1] + sorted_nums[right][1]
            if current_sum == target:
                return [sorted_nums[left][0], sorted_nums[right][0]]
            elif current_sum < target:
                left += 1
            else:
                right -= 1
        
        return [-1, -1]

这个方法需要 O(n log n) 时间排序,还要 O(n) 空间保存原始下标。在本题场景下不如哈希表解法,但作为"有序数组求两数之和"的标准解法,理解它的思路是有价值的。


复杂度对比

解法 时间复杂度 空间复杂度 一万个元素时的操作数
暴力枚举 O(n²) O(1) ~50,000,000 次
哈希表 O(n) O(n) ~10,000 次
双指针 O(n log n) O(n) ~130,000 次

上面说的重复问题,在这个数据规模下意味着:从 5000 万次运算降到 1 万次,差了 5000 倍。

这道题表面上是某个特定问题的解法,但真正值得带走的是一套通用的分析思路------


这道题教会了我们什么

核心思想一:空间换时间(Space-Time Tradeoff)

哈希表的本质是用额外的空间来记录历史信息,从而避免重复计算

这不是"偷懒",而是一种系统性的设计选择------当时间成本远高于空间成本时,多花一点内存来大幅降低时间复杂度,是非常值得的。

在本题里,哈希表用 O(n) 的额外空间,把时间从 O(n²) 降到 O(n)。当 n 足够大时,这个交换是绝对划算的。

核心思想二:改变问法,改变复杂度

很多时候,问题本身没有错,错的是我们问问题的方式

  • 暴力法在问:"哪两个数相加等于 target?"------这需要枚举所有组合。
  • 哈希表法在问:"target 减去当前数,之前出现过吗?"------这只需要一次查找。

当你发现某个操作反复出现且代价很高,问问自己:能不能换一种问法,让这个操作变成 O(1)?

实际应用场景

场景一:数据库索引

没有索引时,查询需要全表扫描 O(n);建了哈希索引后,查询变成 O(1)。本质和这题一模一样------用额外的存储空间(索引),避免每次都从头找。

场景二:LRU 缓存

LRU 缓存需要在 O(1) 时间内完成「查找某个 key 是否存在」和「更新某个 key 为最近使用」。只靠双向链表做不到 O(1) 查找,必须配合哈希表------用空间换时间,这正是本题思想的延伸。


真正有力的总结是:算法不只是写代码,而是学会如何"重新定义问题"。

相关推荐
遥感知识服务11 小时前
Sentinel-1 + DEM + FwDET + 随机森林:从快速水深初估到多因子误差修正
算法·随机森林·sentinel
来一碗刘肉面11 小时前
顺序表与链表的比较
数据结构·算法·链表
alphaTao11 小时前
LeetCode 每日一题 2026/7/13-2026/7/19
算法·leetcode
nnerddboy11 小时前
脑电信号处理实战 03 | 运动想象脑机接口入门:ERD/ERS、CSP 空间滤波与左右拳想象解码
算法·信号处理
QN1幻化引擎13 小时前
Dalin L — 我造了一门支持中文编程的语言,完整移植到 Rust 了
人工智能·算法·机器学习
Rainy Blue88313 小时前
C转C++速成
c语言·c++·算法
txzrxz13 小时前
二分图详解
数据结构·c++·算法·图论·深度优先搜索·二分图染色
gaosushexiangji13 小时前
数字图像相关DIC系统在结构振动模态测量中的应用
算法
KaMeidebaby13 小时前
卡梅德生物技术快报|如何制备单克隆抗体:小众禽类靶点单抗制备实操流程:双载体抗原交叉筛选完整工艺记录
人工智能·python·深度学习·算法·机器学习
北域码匠14 小时前
Karatsuba乘法超详细解析(原理+流程+性能分析+纯原生C#无第三方库完整实现)
算法·c#·大数乘法·高精度计算·数据结构与算法·分治算法·karatsuba 乘法