前言
這道題是Leetcode的第一題,也是經典題目之一,幾乎所有刷題網站的第一題都是“兩數之和”,只是Leetcode這道題不一樣。
在這篇部落格中,我們介紹了兩種解法:
暴力演算法 | 雜湊表演算法 |
---|---|
\(\mathcal{O}(n^2)\) | \(\mathcal{O}(n)\) |
題目描述
給定一個整數陣列 nums
和一個整數目標值 target
,請你在該陣列中找出和為目標值 target
的那兩個整數,並返回它們的陣列下標。
你可以假設每種輸入只會對應一個答案。但是,陣列中同一個元素在答案裡不能重複出現。
你可以按任意順序返回答案。
示例1:
輸入:nums = [2,7,11,15], target = 9
輸出:[0,1]
解釋:因為 nums[0] + nums[1] == 9 ,返回 [0, 1]
示例2:
輸入:nums = [3,2,4], target = 6
輸出:[1,2]
示例2:
輸入:nums = [3,3], target = 6
輸出:[0,1]
提示
- \(2 \leq nums.length \leq 10^4\)
- \(-10^9 \leq nums[i] \leq 10^9\)
- \(-10^9 \leq target \leq 10^9\)
- 只會存在一個有效答案
題目分析
由於題目描述得比較清晰,所以不用過多分析。需要注意的是,題目已經說明每種輸入只對應一個答案,說明我們不用考慮有多個答案的情況。
暴力演算法
閱讀完題目之後,首先能想到的解法就是確定一個值 \(nums[i] , 0 \leq i \leq nums.length-1\) ,在剩餘的序列中找到 \(target-nums[i]\) 。
解題程式碼
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
for (int i = 0 ; i < nums.size() - 1 ; i ++) {
/* j 從 i+1 開始,是因為兩個下標不能相同,題目有說明*/
for (int j = i + 1 ; j < nums.size() ; j++) {
if ( nums[j] == target - nums[i]) {
return {i , j};
}
}
}
return {};
}
};
複雜度分析
設 \(nums\) 的長度為 \(n\) ,可以得到等式
因此\(\mathcal{O}(Complex) = \mathcal{O}(\frac{1}{2}n^2 -\frac{1}{2}n) = \mathcal{O}(n^2)\)
我們在剛開始刷題的時候,可以先試著自己寫出暴力演算法,然後分析一下時間複雜度,再嘗試其他解題方法。
由暴力演算法可以得知,主要的時間消耗是在尋找 \(target-nums[i]\) 上,如果能降低尋找 \(target-nums[i]\) 的時間,就能實現一個時間複雜度小於 \(\mathcal{O}(n^2)\) 的演算法。
因此我們考慮用 雜湊表 來解決這一問題 。
雜湊表演算法
最開始接觸到雜湊表思想是從桶排序演算法,它的典型思想是以空間換時間,雜湊表的時間複雜度為\(\mathcal{O}(1)\) 。
我們可以將陣列分為兩部分,如下圖:
解題程式碼
class Solution {
public:
vector<int> twoSum(vector<int>& nums, int target) {
unordered_map<int,int> idx;
for (int j = 0 ; j < nums.size() ; j++) {
auto it = idx.find(target-nums[j]);
if ( it != idx.end()) {
return { it->second , j};
}
idx[nums[j]] = j;
}
return {};
}
};
複雜度分析
設 \(nums\) 的長度為 \(n\) ,可以得到時間複雜度等式
空間複雜度為 \(\mathcal{O}(n)\).