序
第一次接触到leetcode的题目,和一位姑娘“饶有兴趣”的聊到了深夜。当时的第一想法就是循环遍历,暴力破解,耿直的很。之后她和我说的哈希表解法也没太明白。
直到昨天,有两个题目,一个三位数,一个四位数。他们说,唉,...有一个说我在力扣解题解不出来,老师你能不能教我暴力解法,诶...帮助治疗一下,我的脱发.
我说可以,你在力扣上挠头死磕练死劲,不好用,他不服气。
诶...我说小朋友,你用三个for循环来解这个题,他解不动。
他说你这也没用。我说我这个有用,这是冲劲儿,传统功夫是讲冲劲儿的,一力降十会。
五位数的力扣秃头怪,都难不住我这一个for循环啊…哈!
他非要和我试试,我说可以。诶…我一说完他啪就站起来了,很快啊!........
哈哈,看到了四数之和的时候,也用的暴力解法,然后超时了........这时候才知道认真看别人的解法,看到的双指针,递归解法很有启发,重新回看两数之和,将想法梳理一遍。
题目
给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个 整数,并返回他们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素不能使用两遍。
来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/two-sum
解法
1.暴力解法
两个for循环,直接KO!
class Solution():
def answer(self,nums,target):
for i in range(len(nums)) :
for j in range(len(nums)):
if nums[i] + nums[j] == target and i != j:
return [i,j]
#run time: 36ms memory consump: 14.9MB
感觉运行时间不太靠谱,提交了两次,一次48ms,一次36ms.平方阶的时间复杂度。
2.指针
看到四数之和的双指针,有考虑两数之和能不能应用同样的方法。
然而指针的移动需要基于数组是有序的,这样指针才能确定移动方向,两数之和返回的是数组下标,如果排序的话,需要重新找回原来的数组下标。
先尝试写一个指针从右往ZUO寻找的:
class Solution():
def answer(self,nums,target):
for i in range(len(nums)-1) :
q = len(nums) - 1
while q > i :
if nums[q] + nums[i] == target :
return [i,q]
else :
q -= 1
#run time: 44ms memory consump: 15MB
平方阶的时间复杂度
尝试两个指针,此时需要对数组进行排序:
class Solution():
def answer(self,nums,target):
numsCopy = nums.copy()
nums.sort()
result = []
p = 0
q = len(nums)-1
while q > p :
if nums[p] + nums[q] == target :
break
elif nums[p] + nums[q] > target :
q -= 1
else :
p += 1
#ZUO 和 右 的指针都可以动
for i in range(len(numsCopy)):
if numsCopy[i] == nums[p] or numsCopy[i] == nums[q]:
result = result + [i]
#找到初始的索引值
return result
#run time: 48ms memory consump: 14.8MB
线性的时间复杂度
3.哈希表
简单说,就是从列表依次取一个值,被目标值减去,得出的结果在已有的哈希表中查找,如果有,那么返回该值的索引和哈希表中相同值的索引号,如果没有,把这个值放入哈希表中,继续重复。
class Solution:
def answer(self, nums, target):
hashtable = dict()
for index, num in enumerate(nums):
if (target - num) in hashtable:
return [hashtable[target - num], index]
hashtable[nums[index]] = index
return []
#run time: 24ms memory consump: 14.7MB
效率提高明显.
4.递归
class Solution():
def twoSum(self,nums,target):
result = []
def find(i,target,solution):
if target == 0 and len(solution) == 2 :
result.append(solution[0])
result.append(solution[1])
return
elif len(solution) > 2 or i >= len(nums):
return
find(i+1,target,solution)
find(i+1,target-nums[i],solution+[i])
find(0,target,[])
return result
#21/52 over time ...
因为这个题目要求返回数组下标,后面的三数之和或者四数之和返回的是数值,可以将其进行排序,然后在递归的时候会进行判断,对于不可能存在结果的分支就直接pass了,而这个就是全部分支都要进行下去,所以可能是这个原因导致超时了。如果对数组进行排序之后,就会好很多,但是需要找回排序之前的索引号。正好题目中有这个假设:每种输入只会对应一个答案,所以:
class Solution():
def twoSum(self,nums,target):
numsCopy = nums.copy()
nums.sort()
result = []
def find(i,target,solution):
if target == 0 and len(solution) == 2 :
result.append(solution[0])
result.append(solution[1])
return
elif len(solution) > 2 or i >= len(nums):
return
if target - nums[i]-nums[-1] > 0 :
find(i+1,target,solution)
elif target-(2-len(solution))*nums[i] < 0 :
return
#没有希望的分支就不在继续递归了
find(i+1,target,solution)
find(i+1,target-nums[i],solution+[nums[i]])
find(0,target,[])
resultNosort = []
for i in range(len(numsCopy)):
if numsCopy[i] == result[0] or numsCopy[i] == result[1] :
resultNosort = resultNosort + [i]
#找到未排序之前的索引号
return resultNosort
#20/52 over time....
所以这么写貌似没有什么太大帮助!!!!