给定一个整数数组 和一个目标值 target
,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。你可以假设每种输入只会对应一个答案。但是,你不能重复利用这个数组中同样的元素。
例如给出一个 target 为 9,nums为 [2, 7, 11, 17] ,那么结果应该返回 [0, 1]。
对于一个算法,我们需要考虑的是它的时间复杂度。以下面的暴力破解算法为例,例如输入的规模,也就是 nums 的长度为 n,外层有 n 层循环,内层平均有 [1 + (n - 1)]/2 = n/2 层循环,那么最坏的情况一共大概需要运行 ,也就是 的时间复杂度。
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
for i in range(len(nums)):
t = target - nums[i]
for j in range(i + 1, len(nums)):
if nums[j] == t:
return [i,j]
我自己另外想了一个,想着是为了比暴力破解法可以更快地找到答案,尤其是数组很长的时候。其思想是,默认这个数组是升序好的,我们从数组两端开始,如果两个值之和为 target 那么就输出,否则进行判断:只可以控制其中一个点移动一个位置,那么移动哪个、朝哪个方向移动才可以使得离我们的目的值更近。乍一看感觉有点像梯度下降,都是在寻找一个方向。从时间复杂度上看,并没有像上个例子一样明显的循环嵌套的结构,不怎么好理解。但仔细一想,从第一步开始,假设数组长度为 n,那么就是 (1, n),表示第一个和最后一个数;接下来可能是 (1, n - 1),这个时候我们已经不可能再回去 (1, n) 的情形了,我们可以把我们的问题变成从第一个数到第 n - 1 个数 这个新数组的求解,用公式表示 ,所以这种解法的时间复杂度为 。
class Solution:
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
left = 0
right = len(nums) - 1
while True:
if nums[left] + nums[right] == target:
return [left, right]
elif nums[left] + nums[right] > target:
m = target - nums[left] - nums[right]
if left > 0:
if abs(nums[left - 1] + nums[right] - target) <= abs(nums[left] + nums[right - 1] - target):
left -= 1
else:
right -= 1
else:
right -= 1
else:
if right < len(nums) - 1:
if abs(nums[left + 1] + nums[right] - target) <= abs(nums[left] + nums[right + 1] - target):
left += 1
else:
right += 1
else:
left += 1
当然了,使用哈希表的话也可以达到 的时间复杂度,主要集中在建立索引上,查询操作是一个 的操作所以可以忽略不计。那么找到一个合适的 hash 函数就是关键,这个问题是个大坑,等下次研究相关的 hash 函数时再另起文章。