接觸了程式碼,那麼演算法始終是繞不開的一個重點。
演算法對於開發人員,在日常之中的作用很大,但是對於測試人員來說,實際編碼中用到的似乎不是很多。
不過,現在大廠的測試開發的面試,演算法是必考的,而且這也的確是你的程式碼功底的一項重要體現,學學沒壞處。
關於演算法的基礎知識,之前自己也買過書,但是學習的斷斷續續的,練習刷題就更加稀少了。
所以,打算日後做一個【簡單演算法】的記錄:
- 第一,是為了梳理解題思路,加深鞏固。
- 第二,在學習解題的過程中,將薄弱的程式碼環節、演算法基礎補全。
- 第三,算是對演算法練習的一個督促。
題目來自LeetCode傳送門,有興趣的童鞋可以到上面刷題練習。
一、題目:兩數之和
描述
給定一個整數陣列 nums
和一個目標值 target
,請你在該陣列中找出和為目標值的那 兩個 整數,並返回他們的陣列下標。
你可以假設每種輸入只會對應一個答案。但是,陣列中同一個元素不能使用兩遍。
示例
給定 nums = [2, 7, 11, 15], target = 9
因為 nums[0] + nums[1] = 2 + 7 = 9
所以返回 [0, 1]
解題
突然有了考試做題的感覺,我覺得首先題目要先審清楚,然後自己嘗試用自己已有的知識去解決。
實在做不出來,也別洩氣,演算法道路一定是曲折的,起碼對我來說是這樣,大佬除外。
另外,不管做不做出來,都要去學習下示例解法,學習解題思路,從中收穫更多。
1. 解法1
我自己嘗試著做,這題因為屬於簡單難度,我用for迴圈的知識進行了暴力破解,程式碼以python3為例:
def twoSum(nums, target):
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
其實就是2次迴圈,最外層的迴圈,從列表第一個開始遍歷,直到最後一個元素,長度就是len(nums)
。
因為題目中說,單元素不能使用兩次,所以內層的迴圈,就從i 之後也就是i+1
開始,直到最後一個元素。
拿到了2個數,就進行相加操作,與target
進行比較,如果相等,就返回出這2個元素的下標。
執行一下:
def twoSum(nums, target):
for i in range(len(nums)):
for j in range(i+1, len(nums)):
if nums[i] + nums[j] == target:
return [i, j]
if __name__ == "__main__":
print(twoSum2([2, 15, 11, 7], 9))
-------------結果----------------------
D:\Programs\Python\Python36\python.exe D:/練習/leecode/two_sum.py
[0, 3]
Process finished with exit code 0
這題雖然我做出來,但是這個解法並不好,如果遇到一個元素很多的列表,並且最後的2個值 之和 等於目標值,那麼這種情況下,
陣列裡的任意2個元素都要匹配比較一次。
時間複雜度就為:O(N^2)。
空間複雜度還好,因為沒去去開闢額外的空間去計算,所以是:O(1)。
關於複雜度的分析,後面單獨寫一篇介紹。
2. 解法2
上面的解法缺點就是在最壞的時候,陣列裡的任意2個元素都要匹配比較一次,那麼就要來解決這個問題。
換個思路來想,遍歷列表的中的元素x,如果列表中存在 target-x
,那麼這2個數的下標就是最終我們要的結果。
官方的建議解法用了雜湊表,對於key-value這樣的儲存形式,x
跟它的下標是對應的,這樣一來,找到target-x
的時間複雜度就變成了O(1)。
所以新的解法就是:
def twoSum2(nums, target):
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中的enumerate()
函式,遍歷出列表裡的元素和下標。
在每一次的遍歷中,就可以用目標值 target
—— 當前元素 num
,判斷這個值 在不在字典裡。
這裡用到的是Python 字典 in 操作符,用於判斷鍵是否存在於字典中。
如果在的話,那就返回 字典裡的 元素以及,下標。
因為剛開始迴圈的的時候,字典裡沒資料,所以當每次迴圈後,我們要把這次迴圈的元素跟它的下標 分別 作為 key和value放到字典裡去。
可以加個列印看下 字典的操作過程:
def twoSum2(nums, target):
hashtable = dict()
for i, num in enumerate(nums):
if target - num in hashtable:
return [hashtable[target - num], i]
hashtable[nums[i]] = i
print(hashtable)
return []
if __name__ == "__main__":
print(twoSum2([4, 15, 3, 7, 2], 9))
=============================結果==============================
D:\Programs\Python\Python36\python.exe D:/練習/leecode/two_sum.py
{4: 0}
{4: 0, 15: 1}
{4: 0, 15: 1, 3: 2}
{4: 0, 15: 1, 3: 2, 7: 3}
[3, 4]
Process finished with exit code 0
最終,分析解法2的複雜度:
時間複雜度—— O(N),N 是列表中的元素數量。對於每一個元素 x,我們可以 O(1) 地尋找 target - x。
空間複雜度—— O(N),其中 N 是陣列中的元素數量。主要是雜湊表的開銷,在空間上的消耗。
其實也不能說解法1就是最爛的,因為演算法沒有最好的演算法,只有最適合的演算法。
隨著需求在空間和時間的取捨的不同,具體決定使用哪種演算法也是不同的。
二、小結
在這個簡單演算法裡,學習和回顧到的知識點:
- 雜湊表的優點
- python的enumerate()函式的運用
- Python 字典 in 操作符,用於判斷鍵是否存在於字典中