两数之和(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) 查找,必须配合哈希表------用空间换时间,这正是本题思想的延伸。


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

相关推荐
吃好睡好便好1 小时前
在Matlab中绘制二维等高线图
开发语言·人工智能·学习·算法·matlab
Mr_pyx1 小时前
LeetCode Hot 100 - 最长递增子序列完全题解
算法·leetcode·职场和发展
Mr_pyx1 小时前
LeetCode Hot 100 - 爬楼梯完全题解
算法·动态规划
z200509301 小时前
今日算法: 二叉搜索树
算法
蝈理塘(/_\)大怨种1 小时前
快速排序的递归与非递归实现
数据结构·算法
吴可可1231 小时前
用Bulge保持多段线圆弧连续性
算法·c#
qq_296553272 小时前
矩阵逆时针旋转90度:三种解法从入门到精通
数据结构·python·算法·面试·矩阵
声声codeGrandMaster2 小时前
seq2seq概念和数据集处理
人工智能·pytorch·python·算法·ai
谙弆悕博士2 小时前
【附C源码】C语言实现散列表
c语言·开发语言·数据结构·算法·散列表·数据结构与算法