[python解法] LEETCODE 演算法題一:two sum
題幹:
Given an array of integers, return indices of the two numbers such that they add up to a specific target.
You may assume that each input would have exactly one solution, and you may not use the same element twice.
Example:
Given nums = [2, 7, 11, 15], target = 9,
Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].
總結一下我解這道題的幾種思路,並附上耗時和分析
注:由於 leetcode 上不同時期得到的耗時有比較大的差別,我的操作時間是2018.11.11左右。
解法一:
class Solution(object): def twoSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[int] """ array_len = len(nums) for i in range(0, array_len-1): for j in range(i+1, array_len): if (nums[i] + nums[j] == target): print "%d, %d" %(i,j) return (i,j)
耗時:3472ms
最簡單的方法,兩級迴圈,暴力破解,通過檢查相加結果是否等於target來判定
解法二:
class Solution(object): def twoSum(self, nums, target): """ :type nums: List[int] :type target: int :rtype: List[int] """ for i in range(len(nums)): tar_num = target - nums[i] for j in range(i+1, len(nums)): if nums[j] == tar_num: return [i,j]
耗時:2832ms
通過減少加法運算的次數,達到提高速度的目的
跟方法一相比,執行加法的次數大幅減少,只在外層迴圈中執行一次減法,在二級迴圈中只比較是否相等
解法三:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
length = len(nums)
for i, num in enumerate(nums):
tar_num = target - num
for j in range(i+1, length):
if nums[j] == tar_num:
return [i,j]
耗時:2472ms
通過減少定址的方式,加快程式執行速度。
將len(nums) 的結果放在一個變數中,而不是在每次進入迴圈的時候去進行運算。由於每進入一次二級迴圈,都要執行一次len(nums) 運算,所以修改之後會對效能有提升。
同時,將一級迴圈中的for i in range(len(nums)): 修改為for i, num in enumerate(nums):
這樣做的目的是避免每次做減法運算的時候,都進行一次取址操作。將運算物件提前準備好,而不是要用到的時候再去取。
> 注:enumerate() 函式用於將一個可遍歷的資料物件(如列表、元組或字串)組合為一個索引序列,同時列出資料和資料下標
解法四:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
for i, num in enumerate(nums):
tar_num = target - num
if (tar_num in nums):
if (i != nums.index(tar_num)):
return [i, nums.index(tar_num)]
耗時:960ms
方法三已經達到了二級迴圈的效能極限(筆者想不到還能怎麼優化了)。如果要增加程式執行速度,必須採用一級迴圈的方式。
將減下來的差值,直接在整個輸入陣列中尋找。如果能找到,且對應的下標不等於本身(即所求的target不等於該數的兩倍),則返回結果
這種方法,將原先第二級迴圈的工作量交給了預設方法“in”來解決,加快了執行速度
解法五:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
tar_arr = []
for i, num in enumerate(nums):
tar_num = target - num
try:
j = tar_arr.index(num)
return [j,i]
except ValueError:
tar_arr.append(tar_num)
耗時:176ms
在上一種方式上的改進。既然是通過在陣列中查詢tar_num,不如新建一個數組,在每次查詢失敗的時候,新增到新陣列中,這樣可以省去查詢的很多時間。如果用index方法找不到變數,python會報ValueError,捕獲這個error並append陣列即可
解法六:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
dicty = {}
for i, num in enumerate(nums):
tar_num = target - num
if tar_num not in dicty:
dicty[num] = i
else:
return [dicty[tar_num], i]
耗時:28ms
用字典代替列表,速度會得到進一步提升。
字典相對於列表的優勢在於:查詢和插入的速度極快,不會隨著key的增加而變慢;
字典相對於列表的劣勢在於:需要佔用大量的記憶體,記憶體浪費多
在本題中使用字典作為查詢和插入媒介,可以極大地提升執行速度
解法六+:
class Solution(object):
def twoSum(self, nums, target):
"""
:type nums: List[int]
:type target: int
:rtype: List[int]
"""
dicty = {}
for i, num in enumerate(nums):
tar_num = target - num
if tar_num in dicty:
return [dicty[tar_num], i]
dicty[num] = i
耗時:20ms
在方法六基礎上稍作修改,將else和if的判斷順序做了掉換。確認tar_num是否在當前字典中,比確認不在要快。確認不在,需要花更多時間才能得到結論。所以該方法可以得到更快的速度
綜上,執行速度從最起始的3472ms,加快到20ms,題一解答完畢。