在 LeetCode 上, “两数之和”(Two Sum) 是最经典的入门算法题之一。它看似简单,却蕴含了重要的编程思想——用空间换时间。今天,我就结合三张代码图,带你深入剖析这道题的三种解法,并揭示背后的核心逻辑。
🌟 问题描述
给定一个整数数组 nums 和一个目标值 target,找出数组中两个数的索引,使得它们的和等于 target。
示例:
nums = [2, 7, 11, 15], target = 9 输出: [0, 1] 因为 nums[0] + nums[1] = 2 + 7 = 9
❌ 方法一:暴力破解 —— O(n²) 时间复杂度
最直观的想法是:遍历每一个元素,再遍历其后的所有元素,看是否有满足条件的组合。
function twoSum(nums, target) {
for (let i = 0; i < nums.length; i++) {
for (let j = i + 1; j < nums.length; j++) {
if (nums[i] + nums[j] === target) {
return [i, j];
}
}
}
}
🔍 分析:
- 时间复杂度:O(n²)
- 空间复杂度:O(1)
- 缺点:效率低,数据量大时性能差。
💡 这种方法虽然能解决问题,但显然不是最优解。
✅ 方法二:哈希表优化 —— O(n) 时间复杂度(ES5 版本)
我们换个思路:把“找和”变成“找差” 。
核心思想:
对于当前元素 nums[i],我们只需要知道 target - nums[i] 是否已经出现在前面的元素中。
如果存在,就找到了答案;否则,将当前元素存入哈希表,继续遍历。
function twoSum(nums, target) {
const diffs = {}; // 使用对象模拟 HashMap
const len = nums.length;
for (let i = 0; i < len; i++) {
const complement = target - nums[i]; // 求差
if (diffs[complement] !== undefined) {
return [diffs[complement], i];
}
diffs[nums[i]] = i; // 存储值 -> 索引
}
}
🔍 原理解析:
- 遍历数组,对每个元素计算
target - nums[i]。 - 查看这个“差值”是否已经在
diffs中出现过。 - 如果有,说明之前某个元素与当前元素相加等于
target。 - 否则,将当前元素及其索引存入
diffs。
💡 优点:
- 时间复杂度:O(n),只遍历一次。
- 空间复杂度:O(n),用于存储哈希表。
- 实现简洁,适合生产环境。
⚠️ 注意:这里使用的是普通对象
{}模拟哈希表,适用于 ES5 环境。
✅ 方法三:哈希表优化 —— O(n) 时间复杂度(ES6 Map 版本)
ES6 提供了原生的 Map 数据结构,更适合做键值映射。
function twoSum(nums, target) {
const diffs = new Map(); // 使用 Map,支持任意类型 key
const len = nums.length;
for (let i = 0; i < len; i++) {
const complement = target - nums[i];
if (diffs.has(complement)) {
return [diffs.get(complement), i];
}
diffs.set(nums[i], i);
}
}
🔍 对比 Object vs Map:
| 特性 | Object | Map |
|---|---|---|
| Key 类型 | 字符串/符号 | 任意类型(包括对象) |
| 性能 | 一般 | 更快 |
| 可迭代 | 需要手动处理 | 直接可迭代 |
| 易读性 | 代码更短 | 方法名清晰(has/get/set) |
✅ 推荐在现代项目中使用
Map,更安全、更高效。
📊 算法对比总结
| 方法 | 时间复杂度 | 空间复杂度 | 是否推荐 |
|---|---|---|---|
| 暴力破解 | O(n²) | O(1) | ❌ 不推荐 |
| 哈希表(Object) | O(n) | O(n) | ✅ 推荐 |
| 哈希表(Map) | O(n) | O(n) | ✅✅ 强烈推荐 |
💡 核心思想提炼
这张图总结得非常好:
“求和变成求差” + “用空间换时间”
- 求和变求差:不要每次都去算
a + b == target,而是先算出target - a,然后查有没有这个值。 - 用空间换时间:通过哈希表存储已访问过的元素,实现 O(1) 查询,从而将整体时间复杂度降到 O(n)。
这就是经典的 “哈希表优化” 思想,也是后续很多算法题的基础。
🚀 进阶思考
1. 能否处理重复元素?
可以!只要保证返回的是正确的索引即可。
2. 数组中有多个解怎么办?
题目通常假设只有一个解,但如果允许多个,可以收集所有结果。
3. 如何处理负数或浮点数?
Map 和 Object 都支持,无需额外处理。
📌 总结
“两数之和”不仅仅是一道题,它是算法思维启蒙的第一课。它教会我们:
不要盲目暴力,要学会用数据结构优化算法。
从 O(n²) 到 O(n),从嵌套循环到哈希表,每一次优化都是一次思维的跃迁。
真正的高手,不是写得多快,而是想得多远。
如果你觉得这篇文章对你有帮助,欢迎点赞、收藏、转发!