1:问题描述
来源:LeetCode
难度:简单
问题详情:
给定一个整数数组nums
和一个整数目标值target
,请你在该数组中找出 和为目标值 target
的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
提示:
- 2 <=
nums.length
<= 104 - 109 <=
nums[i]
<= 109 - 109 <=
target
<= 109
只会存在一个有效答案
进阶:你可以想出一个时间复杂度小于 O( n 2 n^{2} n2)的算法吗?
示例 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:暴力for循环
一个最简单的也是最直接想到的解法,是利用双层for循环遍历查询当前索引和其之后的数相加是否满足target
。
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
for i in range(0, len(nums)-1):
for j in range(i+1, len(nums)):
sum_result = nums[i] + nums[j]
if sum_result == target:
return [i, j]
接下来逐句分析一下这种解法的时间复杂度。
for i in range(0, len(nums)-1)
第一个for循环是从0 -> n-2
共循环n-1
次;其中n
指的是列表nums
的长度;for j in range(i+1, len(nums))
第二个for循环是从i+1 -> n-1
共循环n-i-1
次;sum_result = nums[i] + nums[j]
分析其执行了多少次,当i=0
时,j:1 -> n-1
共n-1
次;当i=1
时,j:2 -> n-1
共n-2
次;…;当i=n-2
时,j:n-1 -> n-1
共1
次;所以其总共执行的次数是一个等差数列,因此由等差数列的求和公式可得,该语句总共执行了Sn=(n-1)*(n-2 + 1)/2=0.5n²-n+0.5
次;if sum_result == target
该判断语句的判断次数与sum_result = nums[i] + nums[j]
的执行次数是相同的,所以同样是0.5n²-n+0.5
次;return [i, j]
该语句在整个程序中只执行1
次;
综上所述,整段代码的最高次项是n²
,因此该程序的时间复杂度是 O(
n
2
n^{2}
n2);
所以虽然该代码思想较为简单、容易想到,但是其时间复杂度无法满足进阶要求。
2:解法1——首尾递进查找
该解法首先利用Python自带的sorted函数排序(默认升序),然后利用首尾指针法,对首尾相加的值判断,如果和大于target
,那就需要将两个数中的最大者变小些,所以将尾部指针左移;相反,如果和小于target
,那就需要将两个数中的最小者变大些,所以将首部指针右移;如果相等,则返回索引。
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
length = len(nums)
sorted_id = sorted(range(length), key=lambda k: nums[k])
head = 0
tail = length - 1
head_idx = sorted_id[head]
tail_idx = sorted_id[tail]
add = nums[head_idx] + nums[tail_idx]
while True:
if add == target:
return [head_idx, tail_idx]
elif add > target:
tail -= 1
elif add < target:
head += 1
head_idx = sorted_id[head]
tail_idx = sorted_id[tail]
add = nums[head_idx] + nums[tail_idx]
接下来分析该解法的时间复杂度。
- 排序代码
sorted_id = sorted(range(length), key=lambda k: nums[k])
使用的是python自带的sorted函数排序,其是 timsort排序 算法,时间复杂度为O(nlogn),关于timsort排序可以参考:timsort - 下面的while循环中最多循环n次,而下面的if else语句也最多执行n次,因此这部分语句的时间复杂度为O(n)
- 时间复杂度大小的比较:
O(1) < O(logn) < O(n) < O(nlogn) <O( n 2 n^{2} n2)< O( n 3 n^{3} n3)< O( 2 n 2^{n} 2n) - 因此本程序的时间复杂度为O(nlogn)
3:解法2——Hash查询
第二种解法已经大幅度降低时间复杂度了,但是还有一种更快的解法:利用Hash查询的方式求解。依次遍历nums
中的数据num
,然后查询target-num
是否在字典的key中,如果在就直接返回它们两个的索引;否则就添加 {数据num:其索引i}
键值对。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashDict = {}
for i, num in enumerate(nums):
if target - num in hashDict:
return [hashDict[target - num], i]
hashDict[nums[i]] = i
return []
接下来分析其时间复杂度。
- 查询一次字典的key的时间复杂度是O(1),最多执行n次,因此时间复杂度O(n).