1. 程式人生 > >【簡單演算法】1.兩數之和,給定整數陣列和目標值,找出陣列中2數之和等於目標值的元素

【簡單演算法】1.兩數之和,給定整數陣列和目標值,找出陣列中2數之和等於目標值的元素

接觸了程式碼,那麼演算法始終是繞不開的一個重點。 演算法對於開發人員,在日常之中的作用很大,但是對於測試人員來說,實際編碼中用到的似乎不是很多。 不過,現在大廠的測試開發的面試,演算法是必考的,而且這也的確是你的程式碼功底的一項重要體現,學學沒壞處。 ![](https://img2020.cnblogs.com/blog/1268169/202012/1268169-20201217142744844-1876337476.png) 關於演算法的基礎知識,之前自己也買過書,但是學習的斷斷續續的,練習刷題就更加稀少了。 所以,打算日後做一個【簡單演算法】的記錄: * 第一,是為了梳理解題思路,加深鞏固。 * 第二,在學習解題的過程中,將薄弱的程式碼環節、演算法基礎補全。 * 第三,算是對演算法練習的一個督促。 題目來自LeetCode[傳送門](https://leetcode-cn.com/),有興趣的童鞋可以到上面刷題練習。 ### 一、題目:兩數之和 #### 描述 給定一個整數陣列 `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 操作符,用於判斷鍵是否存在於