给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
示例 1:
输入:nums = [2,7,11,15], target = 9
输出:[0,1] 解释:因为 nums[0] + nums[1] ==9 ,返回 [0, 1] 。
示例 2:
输入:nums = [3,2,4], target = 6
输出:[1,2]
示例 3:
输入:nums = [3,3], target = 6
输出:[0,1]
提示:
2 <= nums.length <= 104
-109 <= nums[i] <= 109
-109 <= target <= 109
只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O(n2) 的算法吗?
一、个人思路
例如对一个长度为4的数组,遍历数组的(i, j)对按照如下顺序对数组进行遍历。一旦发现nums[i]+nums[j]==target,立即return。从而保证只存在一个有效答案。
[0,3]
[0,2]
[0,1]
[1,3]
[1,2]
[2,3]
1.1 Python
1.1.1 语法知识点
- 函数语法
range(start, stop[, step])
参数说明:
start: 计数从 start 开始。默认是从 0 开始。例如range(5)等价于range(0, 5);
stop: 计数到 stop 结束,但不包括 stop。例如:range(0, 5) 是[0, 1, 2, 3, 4]没有5
step: 步长,默认为1。例如:range(0, 5) 等价于 range(0, 5, 1);步长为负数时可以从后往前遍历,例如:range(10, 0 , -1)是[10,9,8,7,6,5,4,3,2,1],末尾不包括0
append() 方法用于在列表末尾添加新的对象。
3. Python enumerate() 函数
-
描述:
enumerate()
函数用于将一个可遍历的数据对象(如列表、元组或字符串)组合为一个索引序列,同时列出数据和数据下标,一般用在 for 循环当中。Python 2.3. 以上版本可用,2.6 添加 start 参数。 -
语法
以下是 enumerate() 方法的语法:
enumerate(sequence, [start=0])
-
参数
- sequence – 一个序列、迭代器或其他支持迭代对象。
- start – 下标起始位置的值。例如,令start=1,则该对象的下标从1开始。
-
返回值
- 返回 enumerate(枚举) 对象。
使用enumerate()和字典dictionary相结合,可以在python中实现Hash表
1.1.2 实现代码
- leetcode上python提交
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
"""
# 外层循环指针i从数组头开始遍历
# 内层指针j从数组尾开始遍历,j的值不能小于或等于i,即要保证j一直在i后面
"""
list_ans=[]
# 不包含nums[len(nums)-1]是因为j指向这个位置,为了避免生成重复数据
for i in range(0,len(nums)-1):
for j in range(len(nums)-1,i,-1):
if (nums[i]+nums[j]==target):
list_ans.append(i)
list_ans.append(j)
return list_ans
- leetcode上python3提交
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
list_ans=[]
# 不包含nums[len(nums)-1]是因为j指向这个位置,为了避免生成重复数据
for i in range(0,len(nums)-1):
for j in range(len(nums)-1,i,-1):
if (nums[i]+nums[j]==target):
list_ans.append(i)
list_ans.append(j)
# 可以不用append()将数据添加到list里再返回
# 可以直接 return [i,j]
return list_ans
# return [] 加了这句又要多额外的内存消耗,不加了
补充: Python3提交时,给的模板函数头上的诸如-> List[int]
等我之前没见过的语句令我非常疑惑,后来查阅得知:
模板里使用了类型标注,具体是 python 标准库 typing 里的 List[1]。
nums: List[int] 标注了 nums 应该是 list,且这个 list 中的元素均为 int。
使用类型标注能增加一些类型检查,更早报告代码问题。
参考
^https://docs.python.org/3/library/typing.html#typing.List
二、 题解
https://leetcode.cn/problems/two-sum/solution/zhu-jian-you-hua-yi-zhi-dao-zui-you-pei-sexli/
官方题解
2.1 暴力解法-线性查找
我的解法和这个类似,虽然我的j是从后往前遍历的,也不是采取比较值的方法,但本质上还是一个时间复杂度为 O ( n 2 ) O(n^2) O(n2)的算法。
2.2 二分查找优化线性查找
线性查找时间复杂度为 O ( n ) O(n) O(n)
二分查找时间复杂度为 O ( l o g n ) O(logn) O(logn)
注: 二分查找的使用前提是,该区间内的数据是有序的。
所以要先对数组进行排序,但是排序后会导致数组每个元素的索引和原来的数字不一样。
在python中,可以把数组构造成字典(Dictionary)键值对,原索引放在key值中。
此时,时间复杂度为
O
(
n
l
o
g
n
)
O(nlogn)
O(nlogn),空间复杂度为
O
(
n
)
O(n)
O(n)。所以此种方法也不是优解。
2.3 哈希查找优化
线性查找时间复杂度为 O ( n ) O(n) O(n)
二分查找时间复杂度为 O ( l o g n ) O(logn) O(logn)
(未发生任何hash碰撞)哈希查找时间复杂度为 O ( 1 ) O(1) O(1)
此时,算法的时间复杂度为 O ( n ) O(n) O(n),空间复杂度为 O ( n ) O(n) O(n)。
2.3.1 思路及算法
使用哈希表,可以将寻找 target - x 的时间复杂度降低到从 O ( n ) O(n) O(n)降低到 O ( 1 ) O(1) O(1)。
这样我们创建一个哈希表,对于每一个 x,我们首先查询哈希表中是否存在 target - x,然后将 x 插入到哈希表中,即可保证不会让 x 和自己匹配。
2.3.2 评论区精选
2.3.3 Python代码
- 用enumerate()
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashtable = dict()
for i, num in enumerate(nums): # 遍历nums列表的下标i和数值num
if target - num in hashtable: # in遍历的是dict中的key值target - num, target - num
return [hashtable[target - num], i]
hashtable[nums[i]] = i # (写完for,思考的时候先写这行)key值存放nums[i],value值存放i
return []
- 不用enumerate()
def twoSum(self, nums: List[int], target: int) -> List[int]:
num_index_map = dict()
for i in range(len(nums)):
x = nums[i]
if (target - x) in num_index_map:
index = num_index_map[target - x]
# 返回的[index,i]应该前一个数小,后一个数大
# 已经存在num_index_map中的index,肯定比才从nums中读入的下标i要小,所以顺序的index在先。
return [index, i]
num_index_map[x] = i
return []