1. 程式人生 > >[python解法] LEETCODE 演算法題一:two sum

[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,題一解答完畢。