力扣題解1-兩數之和

小怪物要打怪發表於2024-07-26

LeetCode 第一題 "兩數之和" (Two Sum) 問題

分析過程:

這個問題可以透過多種方法解決,包括暴力解法和使用雜湊表的解法。以下是詳細的分析過程:

  1. 暴力解法

    • 遍歷陣列中的每一對元素,檢查它們的和是否等於目標值。
    • 時間複雜度是 O(n^2),其中 n 是陣列的長度。
  2. 使用雜湊表

    • 使用一個雜湊表來儲存陣列中的每個元素及其對應的索引。
    • 在遍歷陣列時,檢查當前元素與目標值之差是否存在於雜湊表中。
    • 如果存在,說明找到了兩個元素,它們的和等於目標值。

雜湊表解法的實現:

def two_sum(nums, target):
    # 建立一個雜湊表(字典)來儲存陣列中的每個元素及其對應的索引
    num_to_index = {}
    
    # 遍歷陣列
    for index, num in enumerate(nums):
        # 計算當前元素與目標值之差
        complement = target - num
        
        # 檢查差值是否存在於雜湊表中
        if complement in num_to_index:
            # 如果存在,返回當前元素的索引和差值對應的索引
            return [num_to_index[complement], index]
        
        # 將當前元素及其索引存入雜湊表
        num_to_index[num] = index

    # 如果沒有找到符合條件的兩個元素,返回空列表
    return []

# 示例
nums = [2, 7, 11, 15]
target = 9
print(two_sum(nums, target))  # 輸出:[0, 1]

詳細步驟:

  1. 初始化一個空的雜湊表 num_to_index
  2. 遍歷陣列 nums,對於每個元素 num 及其索引 index
    • 計算 complement = target - num
    • 檢查 complement 是否在雜湊表 num_to_index 中:
      • 如果在,返回 [num_to_index[complement], index]
      • 如果不在,將 num 及其 index 存入雜湊表 num_to_index
  3. 如果遍歷結束後仍未找到符合條件的元素對,返回空列表。

這種方法的時間複雜度是 O(n),空間複雜度也是 O(n),比暴力解法更高效。

雜湊表的基本概念:

雜湊表(Hash Table)是一種高效的資料結構,主要用於快速查詢、插入和刪除操作。雜湊表使用鍵-值對(key-value pairs)來儲存資料,透過一個稱為雜湊函式(hash function)的函式將鍵對映到表中的一個位置(陣列的索引)。這種對映使得查詢和插入操作的平均時間複雜度接近於 O(1)。

  1. 雜湊函式(Hash Function)

    • 雜湊函式將輸入的鍵轉換為陣列的索引。
    • 一個好的雜湊函式應當能將鍵均勻地分佈到雜湊表中,以減少衝突。
  2. 衝突(Collision)

    • 當兩個不同的鍵被雜湊函式對映到相同的索引時,稱為衝突。
    • 解決衝突的方法有多種,包括鏈地址法(Separate Chaining)和開放地址法(Open Addressing)。
  3. 負載因子(Load Factor)

    • 負載因子是雜湊表中已用槽的比例,計算方法是 已儲存元素個數 / 雜湊表的總槽數
    • 負載因子過高時,衝突增多,效能下降;負載因子過低時,空間浪費。

雜湊表的實現方法:

  1. 鏈地址法(Separate Chaining)

    • 每個雜湊表的槽(bucket)都是一個連結串列。
    • 當衝突發生時,新的元素被插入到連結串列的末尾。
    • 查詢時,需要遍歷該連結串列以找到目標元素。
    • 在 Python 中,字典(dict)和集合(set)的實現中使用了鏈地址法。
    # 鏈地址法示例
    hash_table = [[] for _ in range(10)]  # 建立一個包含10個槽的雜湊表,每個槽是一個空列表
    
    def insert(hash_table, key, value):
        index = hash(key) % len(hash_table)
        hash_table[index].append((key, value))
    
    def get(hash_table, key):
        index = hash(key) % len(hash_table)
        for k, v in hash_table[index]:
            if k == key:
                return v
        return None
    
    # 插入和查詢示例
    insert(hash_table, 'apple', 1)
    insert(hash_table, 'banana', 2)
    print(get(hash_table, 'apple'))  # 輸出: 1
    print(get(hash_table, 'banana'))  # 輸出: 2
    
  2. 開放地址法(Open Addressing)

    • 當衝突發生時,使用探測(probing)的方法在雜湊表中尋找下一個可用槽。
    • 常見的探測方法有線性探測(Linear Probing)、二次探測(Quadratic Probing)和雙重雜湊(Double Hashing)。
    # 開放地址法示例(線性探測)
    hash_table = [None] * 10  # 建立一個包含10個槽的雜湊表
    
    def insert(hash_table, key, value):
        index = hash(key) % len(hash_table)
        while hash_table[index] is not None:
            index = (index + 1) % len(hash_table)
        hash_table[index] = (key, value)
    
    def get(hash_table, key):
        index = hash(key) % len(hash_table)
        while hash_table[index] is not None:
            k, v = hash_table[index]
            if k == key:
                return v
            index = (index + 1) % len(hash_table)
        return None
    
    # 插入和查詢示例
    insert(hash_table, 'apple', 1)
    insert(hash_table, 'banana', 2)
    print(get(hash_table, 'apple'))  # 輸出: 1
    print(get(hash_table, 'banana'))  # 輸出: 2
    

雜湊表的優缺點:

優點

  • 快速的查詢、插入和刪除操作,平均時間複雜度為 O(1)。
  • 適用於需要頻繁查詢操作的資料場景。

缺點

  • 需要一個好的雜湊函式來減少衝突。
  • 雜湊表的效能在最壞情況下(所有鍵都衝突)可能退化到 O(n)。
  • 雜湊表需要更多的記憶體來儲存空槽和解決衝突的資料結構。

Python 中的雜湊表:

在 Python 中,字典(dict)和集合(set)都是透過雜湊表來實現的。它們提供了快速的查詢、插入和刪除操作,非常適合需要高效資料存取的場景。

# 字典示例
my_dict = {'apple': 1, 'banana': 2}
print(my_dict['apple'])  # 輸出: 1
my_dict['cherry'] = 3  # 插入新元素
print(my_dict['cherry'])  # 輸出: 3

# 集合示例
my_set = {'apple', 'banana'}
print('apple' in my_set)  # 輸出: True
my_set.add('cherry')  # 插入新元素
print(my_set)  # 輸出: {'cherry', 'banana', 'apple'}

'enumerate'函式

在前面 two_sum 函式的實現中,enumerate 用於遍歷 nums 列表,同時獲取每個元素的索引和值,這樣就可以在雜湊表中儲存元素值及其對應的索引,並在查詢時使用。

enumerate 是 Python 內建函式,用於遍歷序列(如列表、元組或字串)時獲取元素及其對應的索引。它返回一個迭代器,每次迭代時生成一個包含兩個元素的元組:當前元素的索引和值。這個函式通常在需要索引和元素值的場景中非常有用,比如在迴圈中。

語法:

enumerate(iterable, start=0)
  • iterable:一個可以迭代的物件,比如列表、元組、字串等。
  • start:可選引數,指定索引的起始值,預設從 0 開始。

示例:

# 示例列表
fruits = ['apple', 'banana', 'cherry']

# 使用 enumerate 獲取索引和值
for index, fruit in enumerate(fruits):
    print(f"Index: {index}, Fruit: {fruit}")

輸出:

Index: 0, Fruit: apple
Index: 1, Fruit: banana
Index: 2, Fruit: cherry

enumerate 的更多用法:

  1. 指定起始索引
    可以透過設定 start 引數來改變起始索引的值。

    for index, fruit in enumerate(fruits, start=1):
        print(f"Index: {index}, Fruit: {fruit}")
    

    輸出:

    Index: 1, Fruit: apple
    Index: 2, Fruit: banana
    Index: 3, Fruit: cherry
    
  2. 與其他迭代器結合
    enumerate 可以與其他迭代器結合使用,例如列表生成器、集合等。

    fruits_set = {'apple', 'banana', 'cherry'}
    for index, fruit in enumerate(fruits_set):
        print(f"Index: {index}, Fruit: {fruit}")
    

    輸出的順序可能與輸入順序不同,因為集合是無序的。

相關文章