这是我参与2022首次更文挑战的第20天,活动详情查看:2022首次更文挑战
大家好,我是前端西瓜哥,今天我们来解析 LeetCode 的第一道题目:两数之和。
暴力破解
最容易想到的方法是双重遍历,用暴力穷举的方式找到答案。
function twoSum(nums: number[], target: number): number[] {
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];
}
}
};
我们用到两个指针,指针 i 每次向后移动,指针 j 都会遍历 i 之后的所有数字拿到 nums[j],然后将它们一个个和 nums[i] 相加看看,看看和等不等于 target。
如果等于 target,返回对应的索引值数组,否则继续尝试,直到遍历结束。
因为题目说了一定能找到,所以我们并不需要返回一个 [-1, -1] 来表示没有找到。但实际开发中并不会这么理想,还是要考虑找不到的情况的。
空间复杂为 O(1)。用了双重循环,因此时间复杂度 O(n^2)。效率很低,很显然并不是最优解。
哈希表
双重遍历的方式低效,因为它多次访问了同一个数组元素。如果我们能够改善一下,每个数组元素只访问一次,效率就能极大提升。
于是我们需要用到一种名为哈希表的数据结构。
哈希表,简单来说,就是可以通过 a 拿到 b,在表现上,是一种映射关系。但它效率很高,时间复杂度是 O(n)。
我们先看看代码实现:
function twoSum(nums: number[], target: number): number[] {
const map: { [key: number]: number } = {};
for (let i = 0; i < nums.length; i++) {
const num = nums[i];
const matchNum = target - num;
if (map[matchNum] !== undefined) { // 看看匹配的另一个数是否已被访问过
return [map[matchNum], i];
}
map[num] = i; // 将当前数字标记为已访问,并记录好它的索引值
}
};
这里用哈希表记录了 数值 -> 索引 的映射关系。
当我们遍历拿到数组元素时,我们看看这个数字 num 对应的另一个数字 target - num 是否在哈希表中。
如果存在,说明它被访问过,然后通过映射拿到对应的索引值,和 num 的索引值一起返回,遍历结束。
否则我们通过将当前的 num 和对应的索引存到哈希表的方式,来标记为已访问。
哈希表解法的时间复杂度 O(n),空间复杂度为 O(n)。