写在前面的话
从今天起,就正式开启自己的刷题之路了。一方面通过刷题来训练自己的思维能力、代码能力,另一方面,也可以很好地弥补自己知识结构的不足,毕竟总是看教材两天就烦了。。。
题目#1—两数之和
给定一个整数数组 nums 和一个整数目标值 target,请你在该数组中找出 和为目标值 target 的那 两个 整数,并返回它们的数组下标。
你可以假设每种输入只会对应一个答案。但是,数组中同一个元素在答案里不能重复出现。
你可以按任意顺序返回答案。
原题见:两数之和。
我自己的思路
既然来写博客,当然是和大家一起分享思路的,所以自己怎么想的就怎么写,哪怕我的方法可能很蠢。。。
拿到这道题,很自然地,恐怕绝大多数的人的反应都是一样的,就是两个for循环,暴力破解法。从第一个开始,判断后面的每一个数和它相加是否能够得到目标值,然后再从第二个开始,以此类推。。。这样的思路恰好是题目要求的 O(n2)的算法复杂度。
所以我的代码就是这么写的:
import numpy as np
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
for i in range(len(nums)-1):
for j in np.arange(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i,j]
else:
continue
虽然通过了,但是执行用时和内存消耗都惨不忍睹。。。分析以上代码的不足:
- 双循环的查找方法算法复杂度太高;
- 第二个for循环直接用range(i+1, len(nums))就可以,非要加一个numpy,自然加大了内存消耗和执行用时;
- 可以取消else的内容;
- 为确保程序安全结束,最后应该有一个 return [];
- len(nums)计算了两次,所以可以计算一次然后分配内存保存成一个变量,后面继续调用,速度比每次都计算要快;
没错,我的代码就是这么失败,但我一直有一个观点,唯有敢于承认失败,才能终止失败。失败之后发现问题,总结问题,才能有所提高!
现在看这5个问题(当然如果我还有什么问题大家可以提出来指正我嗯),其中后4个,都是治标不治本,即便我修正了,对算法的性能也没有本质的提升,所以,在自己的程序性能并不好的时候,一定要去看看大神们都是怎么写的,多学习,多借鉴大神们的思路,才能快速成长!
官方推荐的代码
以下是LeetCode官方推荐代码,其链接为:LeetCode题目一官方代码。
class Solution:
def twoSum(self, nums: List[int], target: int) -> List[int]:
hashtable = dict()
for i, num in enumerate(nums):
if target - num in hashtable:
return [hashtable[target - num], i]
hashtable[nums[i]] = i
return []
它是用哈希表完成的!
哈希表,又称散列表,Python里实现的时候把这个东西叫字典,以键值对的形式来实现,一个键名对应一个值,可以快速查找对象。关于Python里字典具体都有哪些用法这里就不赘述了,不了解的大家可以通过其他渠道学习一下。
字典这个东西,我自己在学习Python的编程的时候,也是学到过的,也知道用它可以提高检索速度,但是真的遇到问题编程的时候,就只知道一个for循环,所以本质上还是不知道,不足够熟悉,不足够了解。
现在我们分析这段代码,它首先建立一个空字典,然后对这个数组开始循环(只循环一次当然会快):判断目标值减去当前值后的数值,是否在字典里,第一次循环时字典是空的当然不在,所以它就把数组中的数值作为键,把数值在数组中的位置索引作为键对应的值,插入到了字典中。直到某一次循环,找到了这个数值,然后直接输出相应的值和索引即可。
我看完之后的第一反应就是,这个算法太秒了,感觉自己好蠢!!!
总结
当我们想从一个数组中查找两个,或某几个有关系的元素时,可以考虑使用哈希表;另外,这个题目中官方代码所使用的这种边插入键值对、边进行检索的方式值得学习,这种方式简化了1个for循环不说,还让整个检索过程变得十分巧妙,大大提高了执行速度。
最后
希望自己可以坚持下去,也欢迎大家留言和我一起讨论题目和代码,可以和大家一起持续进步,加油!