# LeetCode 1. Two Sum https://leetcode.com/problems/two-sum/description/ ## 題目大意 要從 array `nums` 中找出一對數字相加恰等於 `target` > 題目有說只會剛好有一組答案 ## 思考 ### Algo 1 最直覺的方法就是暴力解: 遍歷 `nums` ,找出兩個數字相加恰等於 `target` ```C++ class Solution { public: vector<int> twoSum(vector<int> &nums, int target) { size_t 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}; } } } }; ``` 然而 Follow-up 希望我們想一個時間複雜度低於 $O(n^2)$ 的演算法,所以還不能交卷 *** ### Algo 2 剛才的找法乍看之下好像就已經直覺到不能再直覺了,~~這種題目竟然是 Easy?還題號 1?~~ 但其實我們稍微改良一下雙迴圈,就能降低時間複雜度了 我們先把 `vec` 做個排序,讓他元素從小到大 然後我們用雙指針的方式,一個從頭開始掃,另一個從尾往回,找到相加等於 `target`就停,交卷 ```C++ class Solution { public: vector<int> twoSum(vector<int> &nums, int target) { size_t n = nums.size(); vector<pair<int, int>> vec; for (int i = 0; i < n; i++) { vec.emplace_back(nums[i], i); } sort(vec.begin(), vec.end()); int i = 0, j = n - 1; while (i <= j) { if (vec[i].first + vec[j].first > target) j--; else if (vec[i].first + vec[j].first < target) i++; else break; } return {vec[i].second, vec[j].second}; } }; ``` 這個演算法的精隨在於從外往內移動指標,不斷縮小搜索範圍,與 algo 1 作對比,algo 1每次檢查完畢,若我們仍未找到所求,我們實際上只是從頭開始一步一步往前走, algo 1豈不是慢太多了嗎? 不過這個方法要確保數列是排序過的,我們假設排序要花費 $O(n\log n)$,建立`vec` 跟雙指標遍歷完都花 $O(n)$,總體而言時間複雜度在 $O(n\log n)$ 挺不錯了,但這種題目總覺得好像能夠 $O(n)$ 內解決吧?讓我們再想想其他演算法 *** ### Algo 3 剛才的方法已經很好了,但我們可以回到真正更直覺的方法(嗯?更直覺結果想不到還叫更直覺嗎...🤔 其實再仔細想想如果要你來挑數字,你會怎麼做? 例如: nums = [2,7,11,15], target = 9 你應該會是這樣思考的: > 第一個挑2,2加另一個數字要是9,那個數字應該是7。找找看有沒有7...... 根據這樣的找法我們可以得出 algo 3 ```C++ class Solution { public: vector<int> twoSum(vector<int> &nums, int target) { unordered_map<int, int> numsMap; for (int i = 0; i < nums.size(); i++) { int temp = target - nums[i]; if (numsMap.find(temp) != numsMap.end()) return {numsMap[temp], i}; numsMap[nums[i]] = i; } } }; ``` 我們在遍歷的過程把元素及其位置記錄下來,如果 `target` 減掉當前的元素 `nums[i]` 有在紀錄表中,表示走到 `nums[i]` 時我們正好找到所求,交卷 (`temp` $+$ `nums[i]` $=$ `target`) 我們假設用 Hash table 查找的時間複雜度可以降低到 $O(1)$ 內解決,這個演算法最壞的情況就是我們要遍歷完數列一次,時間複雜度即 $O(n)$