LeetCode[001]:兩數之和
阿新 • • 發佈:2018-12-11
一、寫在前面
- 初衷 想刷leetcode也不是一兩天的事情了,之前也有很多人給過建議,於是乎,就給安排上了,一來演算法的確是很重要的一塊,需要好好學,為了提升自己,再者,這也可以作為微信推文的一塊,給大家分享,當然,最重要的是這個過程中會結交到很多志趣相投,有想法的朋友。
- 我們 目前我已經集結了7位研究生學長學姐(各個大學),一個月內不會加人,大家可以加我微信(zs820553471),進學習交流群討論,一個月後如果效果不錯,很多人感興趣,我再另建一個專門的演算法學習交流群。
- 安排 目前打算一個星期刷2-3個題,推文分享進度可能會慢一點,但一個星期至少也會有兩篇,期待大家參與。
二、今日題目
給定一個整數陣列和一個目標值,找出陣列中和為目標值的兩個數。 你可以假設每個輸入只對應一種答案,且同樣的元素不能被重複利用。
示例:
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
三、 分析
這個題目看似很簡單,在一個列表(nums
)裡面找到兩數,滿足和為事先指定好的數(target
),其實有陷阱,第一:很多人一看到題目自然想到兩層for迴圈解決問題,but
這種人人都想到的問題你若是也這麼做,如果你就這種程度,面試失敗也不算虧;第二:這題的返回值到底是什麼?你看清了嗎?它的返回值是一個列表,列表裡是int
下標
,你中招了嗎?第三:雙for迴圈極易出錯的地方,不能出現自己加自己的情況。
四、解題
- 方法一: 又蠢又笨的雙重for迴圈
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
l = len(nums)
for a in range(l):
for b in range(l):
if a != b:
if nums[a]+nums[b] == target:
return [a,b]
nums = [2, 7, 11, 15]
target = 9
test_o = Solution()
result = test_o.twoSum(nums,target)
print(result)
-
提交結果:
-
方法二:比雙for聰明一點
我們作圖分析易發現,其實直接雙重for迴圈進行運算是有一半的運算是沒有意義的,比如a+b
和b+a
其實是一模一樣的,如下圖分析:
如何把重複的去掉減少計算機執行量來提升執行速度呢?
我想的比較簡單,利用標識位,建立一個和給定整數列表一樣長的陣列,初始值為全為0,第一層for迴圈執行一次,該位對應的標識值由0變成1,在第二層for運算時先判斷對應的資料位上的識別符號是否都為0,為0 則進行運算比較,否則說明互相之間已經運算過,就continue
,程式碼如下:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
flags = [0, 0, 0, 0]
l = len(nums)
for a in range(l):
flags[a]=1
for b in range(l):
if flags[b]==0:
if nums[a]+nums[b] == target:
return [a,b]
# 另一種簡單方法
# l = len(nums)
# for a in range(l):
# for b in range(a+1,l):
# if nums[a] + nums[b] == target:
# return [a, b]
- 執行結果:
- 方法三 一層迴圈(偷瞄了小詹學長的方法)
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
flags = [0 for i in range(len(nums))]
l = len(nums)
for a in range(l):
one_unm = nums[a]
other_one = target - one_unm
if other_one in nums:
b = nums.index(other_one)
if a != b:
if a>b:
return [b,a]
return [a,b]
- 執行結果:
- 方法四:一層for迴圈優化(小詹讀者【村前河水流】提供)
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
flags = [0 for i in range(len(nums))]
l = len(nums)
dict_nums = {nums[i]:i for i in range(l)}
for a in range(l):
one_unm = nums[a]
other_one = target - one_unm
if other_one in dict_nums and a!= dict_nums[other_one]:
return [a,dict_nums[other_one]]
- 執行結果:
五、疑惑
方法一和二耗時長,雙重for迴圈,時間複雜度高,耗時長沒話說,可方法三和方法四的差別,怎麼也這麼大呢? 我仔細研究了一下,方法三和四最大的差別就是:前者是列表遍歷查詢,後者是字典遍歷查詢,那麼關鍵點來了,到底是不是這個問題呢?
- 列表與字典遍歷效能測試程式碼:
import time
list_01 = [str(i) for i in range(1000)]
start_time = time.time()
if 'a' in list_01 :
a = 0
end_time = time.time()
print("列表遍歷耗時:"+str(end_time-start_time))
dict_01 = {str(i):i for i in range(1000)}
start_time2 = time.time()
if 'a' in dict_01 :
a = 0
end_time2 = time.time()
print("字典遍歷耗時:"+str(end_time2-start_time2))
- 測試結果:
資料量 | 列表耗時 | 字典耗時 |
---|---|---|
1000 | 0 | 0 |
10000 | 0 | 0 |
50000 | 0.996ms | 0 |
100000 | 1.995ms | 0 |
1000000 | 20.944ms | 0 |
10000000 | 208.443ms | 0 |
注:0表示的是所花時間極少,我所測的資料可以精確到微秒,極少的意思可表示為少於1微秒。 通過上表很容易看出,列表遍歷與字典遍歷的天大差別了,不過值得一提的還有,字典生成上要比列表生成慢很多,大家可以自測一下。
六、結語
本來今天打算更新邊學敲邊記爬蟲系列的,但是,突然對演算法來了興趣,於是乎,便有了這個,希望能有更多的同學加入進來。