1502 words
8 minutes
Leetcode Hot 100 两数之和

Leetcode Hot 100 开刷,第一站就来到这位算法圈老熟人:两数之和

这题名气很大,面试里常见,刷题时也常被拿来当热身题。看着像开胃菜,实际上很适合拿来练两种经典思路:

  • 排序 + 相向双指针
  • 哈希表一次遍历

一道题,两种路子。 一个偏套路拆解,一个偏效率直给。 都值得记,别让它白来。

题目链接#

LeetCode 1. 两数之和

题目描述#

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

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

两数之和题目截图

解法一:排序后使用相向双指针#

这题最经典的做法其实是哈希表,一遍遍历直接找补数,时间复杂度可以做到 O(n)

不过我先写的是另一种也很值得掌握的思路:

先保留每个元素的原始下标,再按数值排序,然后用左右双指针向中间逼近。

因为双指针一般要求数组有序,所以我们不能直接在原数组上左右夹击。 如果直接排序,原始下标又会丢失,所以要先把每个元素包装成:

[数值, 原下标]

比如:

nums = [2, 7, 11, 15]
target = 9

预处理后会变成:

[[2, 0], [7, 1], [11, 2], [15, 3]]

这样就算后面排序了,也还能通过第二项拿回原数组下标。

代码实现#

class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
tmp_nums = [[i, idx] for idx, i in enumerate(nums)]
# 排序后使用双指针
tmp_nums = sorted(tmp_nums, key=lambda x: x[0])
n = len(nums)
left, right = 0, n - 1
while left < right:
x = tmp_nums[left][0] + tmp_nums[right][0]
if x > target:
right -= 1
elif x < target:
left += 1
else:
return [tmp_nums[left][1], tmp_nums[right][1]]
return [-1, -1]

思路解析#

双指针的核心逻辑很朴素:

  • 如果当前两数之和 大于 target,说明和太大了,右指针左移
  • 如果当前两数之和 小于 target,说明和太小了,左指针右移
  • 如果刚好等于 target,直接返回这两个数对应的原始下标

拿样例来说:

nums = [2, 7, 11, 15]
target = 9

排序后:

[[2, 0], [7, 1], [11, 2], [15, 3]]

初始时:

  • left = 0,指向 2
  • right = 3,指向 15

先算:

2 + 15 = 17 > 9

和太大,右指针左移。

再算:

2 + 11 = 13 > 9

还是太大,右指针继续左移。

再来:

2 + 7 = 9

命中目标,返回原始下标:

[0, 1]

为什么这种方法可行#

因为排序之后,数组具备了单调性。

设当前和为:

tmp_nums[left][0] + tmp_nums[right][0]
  • 若和偏大,右边元素太大,右指针左移有机会减小总和
  • 若和偏小,左边元素太小,左指针右移有机会增大总和

这就是双指针能成立的基础。

复杂度分析#

  • 时间复杂度: O(n log n),主要消耗在排序上
  • 空间复杂度: O(n),因为额外创建了一个保存“数值 + 原下标”的数组

这种写法的关键点#

1. 排序后原下标会丢失#

所以必须提前记录每个元素在原数组中的位置。

2. 双指针必须建立在有序数组上#

原数组无序时,不能直接左右夹击,否则移动指针没有依据。

解法二:哈希表#

上面那种写法适合拿来练“排序 + 双指针”的套路。 但如果要追求更优时间复杂度,这题最经典的答案还是:哈希表一次遍历

核心思路是这样的:

  • 遍历数组中的每个数 i
  • 先看看它是不是之前某个数正在等的“补数”
  • 如果是,直接返回答案
  • 如果不是,就把 target - i 记进哈希表里,表示“我在等这个数出现”

代码实现#

class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
hashtable = dict()
for idx, i in enumerate(nums):
if i in hashtable:
return [hashtable[i], idx]
hashtable[target - i] = idx
return [-1, -1]

思路解析#

这个写法很妙,妙在它不是把“已经出现过的值”塞进去, 而是把未来希望遇到的补数塞进去。

比如:

nums = [2, 7, 11, 15]
target = 9

遍历过程如下:

第一步:遍历到 2#

  • 当前值是 2
  • 它不在哈希表里
  • 说明之前没人等它
  • 那就把 target - 2 = 7 记进去

此时哈希表为:

{7: 0}

意思就是:

“我现在记住了,下次如果看到 7,就能和下标 0 的 2 凑成答案。”

第二步:遍历到 7#

  • 当前值是 7
  • 发现 7 已经在哈希表里
  • 说明之前的 2 正在等它

于是直接返回:

[0, 1]

一遍结束,干净利索,不绕弯子。

复杂度分析#

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

为什么哈希表更常见#

因为它快。

排序 + 双指针的做法,本质上是先把路修平再开车; 哈希表则更像看见目标就直接踩油门。

如果只是为了通过这道题,哈希表通常是更推荐的标准答案。

两种解法对比#

解法时间复杂度空间复杂度特点
排序 + 双指针O(n log n)O(n)适合练习有序数组上的双指针思想
哈希表O(n)O(n)更经典、更高效、面试中更常见

小结#

这道题不难,但很适合拿来建立题感。

  • 想练套路,可以写 排序 + 双指针
  • 想要高效,可以写 哈希表一次遍历

如果只记一句话,那就是:

双指针靠有序,哈希表靠补数。

Hot 100 第一题,先把手感找回来。 后面继续刷,别怂,题海虽深,慢慢捞也能捞出虾味人生。🦐

Leetcode Hot 100 两数之和
https://ssonnyboy.github.io/yoroziya/posts/leetcode-hot-100-two-sum/
Author
biabuluo
Published at
2026-03-27
License
CC BY-NC-SA 4.0