算法:两数之和
题目
给定一个整数数组 nums
和一个整数目标值 target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案,并且你不能使用两次相同的元素。
你可以按任意顺序返回答案。
示例 1:
1 | 输入:nums = [2,7,11,15], target = 9 |
示例 2:
1 | 输入:nums = [3,2,4], target = 6 |
示例 3:
1 | 输入:nums = [3,3], target = 6 |
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
- 只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2)
的算法吗?
方法一:暴力遍历
1 | func twoSum(_ nums: [Int], _ target: Int) -> [Int] { |
下面逐行解释这段代码的思路:
外层循环(枚举所有数字及其索引)
1
for (i, num) in nums.enumerated() {
这一层循环遍历数组
nums
中的每个元素,并记录下当前元素num
以及对应的索引i
。计算补数
1
let complement = target - num
对于当前元素
num
,通过target - num
计算得到另一个数(称为补数,即 complement),其作用是希望能与num
组合起来使其和为target
。内层循环(再次遍历数组查找补数)
1
for (j, otherNum) in nums.enumerated() where i != j {
这一层循环再次遍历整个数组,寻找满足条件的另一个数字
otherNum
。注意,这里使用了where i != j
条件,确保不会使用同一个元素两次。检查是否找到匹配的补数
1
2
3if otherNum == complement {
return [i, j]
}如果在内层循环中发现
otherNum
等于之前计算的complement
,则说明找到了两个数,它们的和等于target
。此时,函数会立即返回一个包含这两个索引[i, j]
的数组。没有找到匹配时返回空数组
1
return []
如果完成所有循环后都没有找到符合条件的一对数字,则返回一个空数组,表示不存在满足条件的两个数。
算法思路总结
- 暴力解法:代码使用了暴力法,两层枚举,每次将数组中任意两个不同的元素配对,判断其和是否等于目标值
target
。 - 时间复杂度:由于使用了嵌套循环,最坏情况下需要检查所有可能的两个数字组合,时间复杂度为 O(n²)。(其中 n 为数组长度)
- 空间复杂度:只使用了常数级别的额外空间,因此空间复杂度为 O(1)。
方法二:借助字典高效遍历
1 | func twoSum(_ nums: [Int], _ target: Int) -> [Int] { |
这个算法是一种解决“两数之和”问题的高效方法,主要利用了哈希表(字典)的查找特性来实现,在一次遍历数组的过程中就能找到答案。
下面详细解释各个步骤:
初始化哈希表
定义一个字典dict
,键是数组中的数值,值是相应的索引:1
var dict = [Int: Int]()
遍历数组并查找匹配
使用enumerated()
方法遍历数组,使得在每一步可以同时获得元素的索引和值:1
for (index, value) in nums.enumerated() {
- 对于当前值
value
,计算其所需的匹配数,即target - value
。 - 在字典中查找这个匹配数:如果找到了匹配的索引
1
2
3if let otherIndex = dict[target - value] {
return [otherIndex, index]
}otherIndex
,说明已经遍历过的某个数nums[otherIndex]
和当前数value
之和正好等于目标值target
,于是返回这两个索引。
- 对于当前值
更新字典
如果没有找到匹配值,则将当前值以及对应的索引添加到字典中:1
dict[value] = index
这样,当后续的数字需要一个与之配对的数字时,就可以在字典中快速查找。
返回结果
如果遍历完整个数组都没有找到满足条件的两个数,则返回空数组:1
return []
算法思路总结
- 利用哈希表(字典)实现:在一次遍历数组的过程中,利用字典来记录已经遍历过的数字及其索引。
- 查找补数:对于当前数字,计算补数(即 target 减去当前数字),然后检查字典中是否存在这个补数。若存在,则说明找到了满足条件的两个数字。
- 时间复杂度:只需一次遍历,每次查找与插入操作均为 O(1),整体时间复杂度为 O(n)。(其中 n 为数组长度)
- 空间复杂度:需要额外使用一个字典存储数字及其索引,因此空间复杂度为 O(n)。(最坏情况字典大小与数组长度相同)