1. 程式人生 > 其它 >Leetcode刷題筆記(python|C++)(1):兩數之和(陣列、雜湊表)

Leetcode刷題筆記(python|C++)(1):兩數之和(陣列、雜湊表)

技術標籤:leetcode刷題leetcode演算法資料結構c++python

兩數之和(陣列、雜湊表)

題目介紹

在這裡插入圖片描述
示例:

輸入:nums = [2,7,11,15], target = 9
輸出:[0,1]
解釋:因為 nums[0] + nums[1] == 9 ,返回 [0, 1] 。

提示:

  • 2 <= nums.length <= 103
  • -109 <= nums[i] <= 109
  • -109 <= target <= 109

只會存在一個有效答案

1.python解法

(1)暴力解法

兩層迴圈,比較簡單,但超出時間限制範圍。

(2)用 Python 中 list 的相關函式求解

方法一:

解題關鍵主要是想找到 num2 = target - num1,是否也在 list 中,那麼就需要運用以下兩個方法:

  • num2 in nums,返回 True 說明有戲
  • nums.index(num2),查詢 num2 的索引
def twoSum(nums, target):
    lens = len(nums)
    j=-1
    for i in range(lens):
        if (target - nums[i]) in nums:
            if (nums.count(target - nums[i]) == 1)&(target - nums[i] == nums[i]):#如果num2=num1,且nums中只出現了一次,說明找到是num1本身。
                continue
            else:
                j = nums.index(target - nums[i],i+1) #index(x,i+1)是從num1後的序列後找num2                
                break
    if j>0:
        return [i,j]
    else:
        return []

執行通過,不過耗時較長,共 1636ms。

方法二:

解題思路是在方法一的基礎上,優化解法。想著,num2 的查詢並不需要每次從 nums 查詢一遍,只需要從 num1 位置之前或之後查詢即可。但為了方便 index 這裡選擇從 num1 位置之前查詢:

def twoSum(nums, target):
    lens = len(nums)
    j=-1
    for i in range(1,lens):
        temp = nums[:i]
        if (target - nums[i]) in temp:
            j = temp.index(target - nums[i])
            break
    if j>=0:
        return [j,i]

執行通過,耗時縮短一半多,共 652ms。

(3)用字典模擬雜湊求解

方法三:

參考了大神們的解法,通過雜湊來求解,這裡通過字典來模擬雜湊查詢的過程。
個人理解這種辦法相較於方法一其實就是字典記錄了 num1 和 num2 的值和位置,而省了再查詢 num2 索引的步驟。

def twoSum(nums, target):
    hashmap={}
    for ind,num in enumerate(nums):
        hashmap[num] = ind
    for i,num in enumerate(nums):
        j = hashmap.get(target - num)
        if j is not None and i!=j:
            return [i,j]

通過字典的方法,查詢效率快很多,執行速度大幅縮短,共 88ms。

方法四:

類似方法二,不需要 mun2 不需要在整個 dict 中去查詢。可以在 num1 之前的 dict 中查詢,因此就只需要一次迴圈可解決。

def twoSum(nums, target):
    hashmap={}
    for i,num in enumerate(nums):
        if hashmap.get(target - num) is not None:
            return [i,hashmap.get(target - num)]
        hashmap[num] = i #這句不能放在if語句之前,解決list中有重複值或target-num=num的情況

不過方法四相較於方法三的執行速度沒有像方法二相較於方法一的速度提升。執行速度在 70ms 多。

2.C++解法

(1)暴力列舉

思路及演算法

最容易想到的方法是列舉陣列中的每一個數 x,尋找陣列中是否存在 target - x。

當我們使用遍歷整個陣列的方式尋找 target - x 時,需要注意到每一個位於 x 之前的元素都已經和 x 匹配過,因此不需要再進行匹配。而每一個元素不能被使用兩次,所以我們只需要在 x 後面的元素中尋找 target - x。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        int n = nums.size();
        for (int i = 0; i < n; ++i) {
            for (int j = i + 1; j < n; ++j) {
                if (nums[i] + nums[j] == target) {
                    return {i, j};
                }
            }
        }
        return {};
    }
};

複雜度分析

  • 時間複雜度:O(N^2),其中 N 是陣列中的元素數量。最壞情況下陣列中任意兩個數都要被匹配一次。
  • 空間複雜度:O(1)。

(2)雜湊表

思路及演算法

注意到方法一的時間複雜度較高的原因是尋找 target - x 的時間複雜度過高。因此,我們需要一種更優秀的方法,能夠快速尋找陣列中是否存在目標元素。如果存在,我們需要找出它的索引。

使用雜湊表,可以將尋找 target - x 的時間複雜度降低到從 O(N)O(N) 降低到 O(1)O(1)。

這樣我們建立一個雜湊表,對於每一個 x,我們首先查詢雜湊表中是否存在 target - x,然後將 x 插入到雜湊表中,即可保證不會讓 x 和自己匹配。

class Solution {
public:
    vector<int> twoSum(vector<int>& nums, int target) {
        unordered_map<int, int> hashtable;
        for (int i = 0; i < nums.size(); ++i) {
            auto it = hashtable.find(target - nums[i]);
            if (it != hashtable.end()) {
                return {it->second, i};
            }
            hashtable[nums[i]] = i;
        }
        return {};
    }
};

複雜度分析

  • 時間複雜度:O(N),其中 N 是陣列中的元素數量。對於每一個元素 x,我們可以 O(1)地尋找 target - x。
  • 空間複雜度:O(N),其中 N 是陣列中的元素數量。主要為雜湊表的開銷。