LeetCode 解題思路:1. Two Sum(兩數之和)
從暴力解到最優解的演進過程
目錄
題目描述
給定一個整數陣列 nums
和一個目標值 target
,請你在該陣列中找出和為目標值的那兩個整數,並返回它們的陣列索引。
你可以假設每種輸入只會對應一個答案。但是,陣列中同一個元素不能使用兩遍。
範例:
輸入:nums = [2,7,11,15], target = 9
輸出:[0,1]
解釋:因為 nums[0] + nums[1] == 9,返回 [0, 1]
解法一:暴力解法(Brute Force)
思路
最直觀的方法是使用兩層迴圈,檢查所有可能的數字組合。
程式碼
def twoSum(nums, target):
n = len(nums)
for i in range(n):
for j in range(i + 1, n):
if nums[i] + nums[j] == target:
return [i, j]
return []
複雜度分析
- 時間複雜度:O(n²)
- 外層迴圈執行 n 次
- 對於每個元素,內層迴圈平均執行 n/2 次
- 總計約 n × n/2 = O(n²)
- 空間複雜度:O(1)
- 只使用了常數個變數(i, j),不隨輸入規模增長
優缺點
- ✅ 簡單直觀,容易理解和實現
- ✅ 不需要額外的空間
- ❌ 時間效率低,當陣列很大時會很慢
解法二:兩次遍歷 Hash Table
思路
利用 Hash Table 將查找時間從 O(n) 降低到 O(1)。
- 第一次遍歷:建立每個數字到其索引的映射
- 第二次遍歷:查找每個數字的補數是否存在
程式碼
def twoSum(nums, target):
# 第一次遍歷:建立 hash table
hashmap = {}
for i in range(len(nums)):
hashmap[nums[i]] = i
# 第二次遍歷:查找補數
for i in range(len(nums)):
complement = target - nums[i]
if complement in hashmap and hashmap[complement] != i:
return [i, hashmap[complement]]
return []
複雜度分析
- 時間複雜度:O(n)
- 第一次遍歷:n 次插入操作,每次 O(1) = O(n)
- 第二次遍歷:n 次查找操作,每次 O(1) = O(n)
- 總計:O(n) + O(n) = O(n)
- 空間複雜度:O(n)
- Hash table 最多存儲 n 個鍵值對
關鍵理解:為什麼 Hash Table 查找是 O(1)?
Hash Table 使用雜湊函數直接計算元素的存儲位置,不需要逐一比對:
查找 7 in hashmap:
1. 計算 hash(7) = 某個位置
2. 直接訪問該位置
3. 不需要遍歷整個 table!
解法三:一次遍歷 Hash Table(最優解)
思路
邊遍歷邊查找,在同一次迴圈中完成:
- 先檢查當前數字的補數是否已在 hash table 中
- 如果不在,將當前數字加入 hash table
程式碼
def twoSum(nums, target):
hashmap = {}
for i in range(len(nums)):
complement = target - nums[i]
# 先查找
if complement in hashmap:
return [hashmap[complement], i]
# 後加入
hashmap[nums[i]] = i
return []
演算過程示例
陣列:[2, 7, 11, 15],目標:9
i=0, nums[0]=2:
- complement = 9-2 = 7
- 7 在 hashmap?否
- 加入:hashmap = {2: 0}
i=1, nums[1]=7:
- complement = 9-7 = 2
- 2 在 hashmap?是!位置 0
- 返回 [0, 1] ✓
複雜度分析
- 時間複雜度:O(n)
- 最壞情況遍歷整個陣列一次
- 每次迭代中的查找和插入都是 O(1)
- 空間複雜度:O(n)
- 最壞情況 hash table 存儲 n-1 個元素
為什麼這樣可行?
關鍵洞察:對於任意一對解 (i, j),當我們遍歷到較大的索引時,較小的索引已經在 hash table 中了!
三種解法比較
解法 | 時間複雜度 | 空間複雜度 | 優點 | 缺點 |
---|---|---|---|---|
暴力解 | O(n²) | O(1) | 簡單、省空間 | 速度慢 |
兩次遍歷 | O(n) | O(n) | 快速 | 需要兩次迴圈 |
一次遍歷 | O(n) | O(n) | 最快、程式碼簡潔 | 需要額外空間 |
重要觀念總結
時間與空間的權衡:我們用 O(n) 的額外空間換取了時間效率從 O(n²) 提升到 O(n)
Hash Table 的威力:將查找操作從 O(n) 降到 O(1) 是許多演算法優化的關鍵
演算法優化的層次:
- 第一步:實現功能(暴力解)
- 第二步:優化時間複雜度(Hash Table)
- 第三步:優化實際執行效率(一次遍歷)
練習建議
- 先自己實現暴力解,確保理解問題
- 思考如何優化查找過程
- 試著手動模擬 Hash Table 的運作
- 比較不同解法在大數據集上的實際執行時間
這道題是理解 Hash Table 應用的經典例題,掌握這三種解法的演進過程,對於解決其他類似問題很有幫助!