1-TwoSum
Posted rongyupan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了1-TwoSum相关的知识,希望对你有一定的参考价值。
两数之和
给定一个整数数组nums和一个目标值target,请你在该数组中找出和为目标值的那两个整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
示例:
给定 nums = [2, 7, 11, 15], target = 9
因为 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。
暴力法
不用任何高难度算法,两遍循环暴力解法。当然,这种解法的效率很低。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
i = 0
n = len(nums) # length of nums
for i in range(0, n-1):
for j in range(i+1, n):
if nums[i] + nums[j] == target:
return [i,j]
break
时间复杂度为 O(N^2); 空间复杂度为 O(1)
两遍哈希表
下面是引用了力扣官方的思路解答:
为了对运行时间复杂度进行优化,我们需要一种更有效的方法来检查数组中是否存在目标元素。如果存在,我们需要找出它的索引。保持数组中的每个元素与其索引相互对应的最好方法是什么?哈希表。
通过以空间换取速度的方式,我们可以将查找时间从O(n)降低到O(1)。哈希表正是为此目的而构建的,它支持以"近似"恒定的时间进行快速查找。我用“近似”来描述,是因为一旦出现冲突,查找用时可能会退化到O(n)。但只要你仔细地挑选哈希函数,在哈希表中进行查找的用时应当被摊销为 O(1)。
一个简单的实现使用了两次迭代。在第一次迭代中,我们将每个元素的值和它的索引添加到表中。然后,在第二次迭代中,我们将检查每个元素所对应的目标元素(target - nums[i])是否存在于表中。注意,该目标元素不能是nums[i]本身!
哈希表查找固定元素更快,这里target-num
就是个相对固定的数值,查找起来会很快。
这个算法的关键在于 翻转哈希表 和 目标元素。
python中,字典可以用作哈希表。Java中,有专门的哈希表类,可以直接调用。
python实现
先介绍两个函数:
dict.get(key, default=None)
- key -- 字典中要查找的键。
- default -- 如果指定键的值不存在时,返回该默认值。
enumerate(sequence, [start=0])
- sequence -- 一个序列、迭代器或其他支持迭代对象。
- start -- 下标起始位置。
简单用法:
seasons = [‘Spring‘, ‘Summer‘, ‘Fall‘, ‘Winter‘]
list(enumerate(seasons))
# return: [(0, ‘Spring‘), (1, ‘Summer‘), (2, ‘Fall‘), (3, ‘Winter‘)]
list(enumerate(seasons, start=1)) # 下标从 1 开始
# return: [(1, ‘Spring‘), (2, ‘Summer‘), (3, ‘Fall‘), (4, ‘Winter‘)]
seq = [‘one‘, ‘two‘, ‘three‘]
for i, element in enumerate(seq):
print i, element
# return:
# 0 one
# 1 two
# 2 three
使用以上两个函数,我们就可以完成这个题目:
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
Hashmap = {}
for index, num in enumerate(nums): # create Hashmap(type=dict)
Hashmap[num] = index # reverse the enumerate(nums)
for i, num in enumerate(nums):
j = Hashmap.get(target - num) # try to find correct index.
if j is not None and j != i:
return [i, j]
这几行代码很巧妙,效率比暴力法时间上提高了100多倍。
enumerate(nums)
: 把List形式的nums转换为 (0, nums[0]), (1,nums[1])...
Hashmap[num] = index
: Hashmap的形式为 {nums[0]:0, nums[1]:1, ...}
也就是对enumerate()
做了一次翻转。这是提升查找效率的关键。
j = Hashmap.get(target - num)
: 这里的 j 是下标(indices, index的复数形式),如果能找到需要的另一个 加数B 的话,j 就为这个加数的下标;反之返回 None。
最后要判断 加数B(j) 和 加数A(i) 相等,然后返回即可。
Java实现
官方给出了Java实现的代码,整体思路和python是一致的。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
map.put(nums[i], i);
}
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement) && map.get(complement) != i) {
return new int[] { i, map.get(complement) };
}
}
throw new IllegalArgumentException("No two sum solution");
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
一遍哈希表
在进行迭代并将元素插入到表中的同时,我们还会回过头来检查表中是否已经存在当前元素所对应的目标元素。如果它存在,那我们已经找到了对应解,并立即将其返回。
也就是一边构造哈希表,一边开始查找
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
Hashmap = {}
for index, num in enumerate(nums):
i = Hashmap.get(target - num)
if i is not None and i != index:
return [i, index]
Hashmap[num] = index
注意: if
循环 要在 Hashmap
赋值之前。
原因: 如果列表出现相同元素,如 nums=[3,3]
, 若先赋值,Hashmap={3:0, 3:1}
是不被允许的,返回 NULL。先if循环就不会出现这个问题。
class Solution {
public int[] twoSum(int[] nums, int target) {
Map<Integer, Integer> map = new HashMap<>();
for (int i = 0; i < nums.length; i++) {
int complement = target - nums[i];
if (map.containsKey(complement)) {
return new int[] { map.get(complement), i };
}
map.put(nums[i], i);
}
throw new IllegalArgumentException("No two sum solution");
}
}
作者:LeetCode
链接:https://leetcode-cn.com/problems/two-sum/solution/liang-shu-zhi-he-by-leetcode-2/
来源:力扣(LeetCode)
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
总结
尽管第一个题被标记为简单,这也只是代表暴力解法是简单的。哈希表是解决查找问题的一大利器。
以上是关于1-TwoSum的主要内容,如果未能解决你的问题,请参考以下文章