大家好,我是前端西瓜哥,今天我们来解析 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(1)
。
我们先看看代码实现:
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)
。