LeetCode 第一題 "兩數之和" (Two Sum) 問題
分析過程:
這個問題可以透過多種方法解決,包括暴力解法和使用雜湊表的解法。以下是詳細的分析過程:
-
暴力解法:
- 遍歷陣列中的每一對元素,檢查它們的和是否等於目標值。
- 時間複雜度是 O(n^2),其中 n 是陣列的長度。
-
使用雜湊表:
- 使用一個雜湊表來儲存陣列中的每個元素及其對應的索引。
- 在遍歷陣列時,檢查當前元素與目標值之差是否存在於雜湊表中。
- 如果存在,說明找到了兩個元素,它們的和等於目標值。
雜湊表解法的實現:
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]
詳細步驟:
- 初始化一個空的雜湊表
num_to_index
。 - 遍歷陣列
nums
,對於每個元素num
及其索引index
:- 計算
complement = target - num
。 - 檢查
complement
是否在雜湊表num_to_index
中:- 如果在,返回
[num_to_index[complement], index]
。 - 如果不在,將
num
及其index
存入雜湊表num_to_index
。
- 如果在,返回
- 計算
- 如果遍歷結束後仍未找到符合條件的元素對,返回空列表。
這種方法的時間複雜度是 O(n),空間複雜度也是 O(n),比暴力解法更高效。
雜湊表的基本概念:
雜湊表(Hash Table)是一種高效的資料結構,主要用於快速查詢、插入和刪除操作。雜湊表使用鍵-值對(key-value pairs)來儲存資料,透過一個稱為雜湊函式(hash function)的函式將鍵對映到表中的一個位置(陣列的索引)。這種對映使得查詢和插入操作的平均時間複雜度接近於 O(1)。
-
雜湊函式(Hash Function):
- 雜湊函式將輸入的鍵轉換為陣列的索引。
- 一個好的雜湊函式應當能將鍵均勻地分佈到雜湊表中,以減少衝突。
-
衝突(Collision):
- 當兩個不同的鍵被雜湊函式對映到相同的索引時,稱為衝突。
- 解決衝突的方法有多種,包括鏈地址法(Separate Chaining)和開放地址法(Open Addressing)。
-
負載因子(Load Factor):
- 負載因子是雜湊表中已用槽的比例,計算方法是
已儲存元素個數 / 雜湊表的總槽數
。 - 負載因子過高時,衝突增多,效能下降;負載因子過低時,空間浪費。
- 負載因子是雜湊表中已用槽的比例,計算方法是
雜湊表的實現方法:
-
鏈地址法(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
-
開放地址法(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
的更多用法:
-
指定起始索引:
可以透過設定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
-
與其他迭代器結合:
enumerate
可以與其他迭代器結合使用,例如列表生成器、集合等。fruits_set = {'apple', 'banana', 'cherry'} for index, fruit in enumerate(fruits_set): print(f"Index: {index}, Fruit: {fruit}")
輸出的順序可能與輸入順序不同,因為集合是無序的。