从今天开始,我将开启「前端算法面试实战」系列,帮助大家攻克前端面试中的算法难题。作为第一篇文章,我选择了一道面试中的"常客"——两数之和问题,这道题易于理解但解法多样,非常适合作为我们的开篇之作。
问题描述
两数之和:给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那两个整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
示例:
输入:nums = [2, 7, 11, 15], target = 9
输出:[0, 1]
解释:因为 nums[0] + nums[1] = 2 + 7 = 9,所以返回 [0, 1]
解题思路
这道题有多种解法,我们从最基础的方法开始,逐步优化至最优解。
方法一:暴力解法
最直观的解法是使用两层循环,枚举所有可能的数字对,判断它们的和是否等于目标值。
function twoSum(nums, target) {
const n = nums.length;
for (let i = 0; i < n; i++) {
for (let j = i + 1; j < n; j++) {
if (nums[i] + nums[j] === target) {
return [i, j];
}
}
}
return []; // 没有找到符合条件的结果
}
- 时间复杂度: O(n2)O(n2),其中 nn 是数组的长度
- 空间复杂度: O(1)O(1),只使用了常数级别的额外空间
方法二:哈希表(一次遍历)
暴力法效率较低,我们可以通过哈希表记录已经遍历过的元素,实现 O(n)O(n) 的时间复杂度。
function twoSum(nums, target) {
const map = new Map();
for (let i = 0; i < nums.length; i++) {
const complement = target - nums[i];
if (map.has(complement)) {
return [map.get(complement), i];
}
map.set(nums[i], i);
}
return []; // 没有找到符合条件的结果
}
- 时间复杂度: O(n)O(n),只需要一次遍历
- 空间复杂度: O(n)O(n),哈希表最多需要存储 nn 个元素
方法三:排序 + 双指针
如果允许修改原数组,我们还可以使用排序 + 双指针的方法:
function twoSum(nums, target) {
// 创建一个数组,存储原始索引
const indices = nums.map((num, index) => ({
value: num,
index: index
}));
// 按照值排序
indices.sort((a, b) => a.value - b.value);
let left = 0;
let right = nums.length - 1;
while (left < right) {
const sum = indices[left].value + indices[right].value;
if (sum === target) {
return [indices[left].index, indices[right].index];
} else if (sum < target) {
left++;
} else {
right--;
}
}
return [];
}
- 时间复杂度: O(nlogn)O(nlogn),主要来自排序操作
- 空间复杂度: O(n)O(n),需要存储原始索引
方法比较与分析
| 方法 | 时间复杂度 | 空间复杂度 | 优点 | 缺点 |
|---|---|---|---|---|
| 暴力解法 | O(n2)O(n2) | O(1)O(1) | 简单直观,无需额外空间 | 效率低 |
| 哈希表 | O(n)O(n) | O(n)O(n) | 效率高,一次遍历 | 需要额外空间 |
| 排序+双指针 | O(nlogn)O(nlogn) | O(n)O(n) | 理解简单 | 需要排序,效率次之 |
对于这道题,哈希表法是最优解,它完美地平衡了时间和空间复杂度。面试中,建议直接使用哈希表法,因为它简洁高效。
进阶思考
-
如果数组已经排序,你会如何解决?
- 使用双指针法,时间复杂度 O(n)O(n),空间复杂度 O(1)O(1)
-
如果需要找出所有和为 target 的不重复数对呢?
- 可以先排序,然后使用双指针法,同时处理重复元素
-
扩展到三数之和、四数之和问题时,思路如何变化?
- 三数之和:可以固定一个数,然后在剩余数组中找两数之和
- 四数之和:可以固定两个数,然后在剩余数组中找两数之和
总结
两数之和是算法面试中的经典问题,也是哈希表应用的典型案例。通过这个问题,我们可以学习到:
- 如何从暴力解法优化到高效解法
- 如何利用哈希表将查找效率从 O(n)O(n) 提升到 O(1)O(1)
- 如何平衡时间复杂度和空间复杂度
在下一篇文章中,我们将探讨另一个前端面试常见算法题:链表的反转。敬请期待!