|
1 | 1 | --- |
2 | | -title: UMPIRE 1.Two-Sum |
3 | | -subtitle: "https://leetcode.com/problems/two-sum/description/" |
4 | | -date: 2025-04-11T20:16:46+08:00 |
5 | | -lastmod: 2025-04-11T20:16:46+08:00 |
6 | | -draft: false |
7 | | -author: "Kimi.Tsai" |
8 | | -authorLink: "https://kimi0230.github.io/" |
9 | | -description: "Two-Sum" |
10 | | -license: "" |
11 | | -images: [] |
12 | | - |
13 | | -tags: [LeetCode, Go, Easy, Array, Hash Table] |
14 | | -categories: [LeetCode] |
15 | | - |
16 | | -featuredImage: "" |
17 | | -featuredImagePreview: "" |
18 | | - |
19 | | -hiddenFromHomePage: false |
20 | | -hiddenFromSearch: false |
21 | | -twemoji: false |
22 | | -lightgallery: true |
23 | | -ruby: true |
24 | | -fraction: true |
25 | | -fontawesome: true |
26 | | -linkToMarkdown: false |
27 | | -rssFullText: false |
28 | | - |
29 | | -toc: |
30 | | - enable: true |
31 | | - auto: true |
32 | | -code: |
33 | | - copy: true |
34 | | - maxShownLines: 200 |
35 | | -math: |
36 | | - enable: false |
37 | | -mapbox: |
38 | | -share: |
39 | | - enable: true |
40 | | -comment: |
41 | | - enable: true |
42 | | -library: |
43 | | - css: |
44 | | - js: |
45 | | -seo: |
46 | | - images: [] |
| 2 | +title: UMPIRE 0001.Two-Sum |
| 3 | +tags: |
| 4 | + - Easy |
| 5 | +author: Kimi Tsai <kimi0230@gmail.com> |
| 6 | +description: UMPIRE Solution for Two Sum problem |
47 | 7 | --- |
48 | | -# Solution - UMPIRE |
| 8 | + |
| 9 | +# UMPIRE 0001.Two-Sum |
49 | 10 |
|
50 | 11 | ## Output 1: UMPIRE 解題(完整思考版) |
51 | 12 |
|
52 | 13 | ### U – Understand(理解題目) |
53 | | -- **題目描述**:給定一個整數陣列 `nums` 和一個目標值 `target`,找出陣列中兩個整數,使其相加等於 `target`,並回傳這兩個數字的索引值。 |
| 14 | +- **題目描述**:給定一個整數陣列 `nums` 和一個目標值 `target`,請在該陣列中找出和為目標值的那兩個整數,並返回它們的陣列下標。 |
54 | 15 | - **關鍵限制**: |
55 | | - - 每種輸入剛好只有一個解。 |
56 | | - - 同一個元素不能使用兩次。 |
57 | | - - 回傳索引的順序不限。 |
| 16 | + - 每種輸入只會對應一個答案。 |
| 17 | + - 不能重複使用陣列中同樣的元素(即同一個 index 不能用兩次)。 |
| 18 | + - 答案可以按任意順序返回。 |
58 | 19 | - **潛在陷阱**: |
59 | | - - 負數的存在(`target` 或 `nums[i]` 可能為負)。 |
60 | | - - 陣列長度最小為 2。 |
61 | | -- **測試案例**: |
62 | | - - **Happy Path**: `nums = [2, 7, 11, 15], target = 9` -> `[0, 1]` |
63 | | - - **Edge Case 1 (重複數字)**: `nums = [3, 3], target = 6` -> `[0, 1]` |
64 | | - - **Edge Case 2 (負數)**: `nums = [-1, -8, 5], target = -9` -> `[0, 1]` |
| 20 | + - 陣列中可能包含負數。 |
| 21 | + - 陣列可能未排序(此題未註明排序,不能直接用雙指針而不先排序)。 |
| 22 | +- **Happy Path**: |
| 23 | + - `nums = [2, 7, 11, 15], target = 9` -> 返回 `[0, 1]`。 |
| 24 | +- **Edge Cases**: |
| 25 | + - `nums = [3, 3], target = 6` -> 返回 `[0, 1]`(數值相同但 index 不同)。 |
| 26 | + - `nums = [3, 2, 4], target = 6` -> 返回 `[1, 2]`(目標值由非連續元素組成)。 |
65 | 27 |
|
66 | 28 | ### M – Match(匹配知識) |
67 | | -- **主要演算法/資料結構**:**Hash Table (Map)**。 |
68 | | -- **為什麼匹配**: |
69 | | - - 暴力法 (O(n²)) 需要兩層迴圈。 |
70 | | - - 使用 Hash Table 可以將查找時間從 O(n) 降低到 O(1),總時間複雜度優化至 O(n)。 |
71 | | -- **其他嘗試**: |
72 | | - - **排序 + 雙指針**: 雖然空間複雜度可降至 O(1),但因為需要回傳原始索引,排序會打亂索引,處理起來較複雜且時間複雜度為 O(n log n),不如 Hash Table 快。 |
| 29 | +- **主要模式**:**Hash Map (Hash Table)**。 |
| 30 | +- **為什麼適合**: |
| 31 | + - 我們需要快速尋找「當前數值的互補值」(即 `target - nums[i]`)是否已經在之前出現過。Hash Map 的查找時間複雜度為 $O(1)$,比起線性尋找的 $O(n)$ 快得多。 |
| 32 | +- **其他方案**: |
| 33 | + - **暴力解 (Brute Force)**:雙層迴圈遍歷所有組合。時間複雜度 $O(n^2)$,效率較低。 |
| 34 | + - **排序 + 雙指針**:先排序再從兩端逼近。時間複雜度 $O(n \log n)$(受限於排序),雖然空間複雜度可降至 $O(1)$,但若題目要求返回原始下標,排序會打亂位置,需額外處理,不如 Hash Map 直觀且快。 |
73 | 35 |
|
74 | 36 | ### P – Plan(制定計畫) |
75 | | -- 建立一個空的 Map `m`,Key 存數字的值,Value 存該數字的索引。 |
76 | | -- 遍歷 `nums` 陣列,對於當前數字 `v` 與索引 `i`: |
77 | | - 1. 計算所需的差值 `complement = target - v`。 |
78 | | - 2. 檢查 `complement` 是否已存在於 Map `m` 中。 |
79 | | - 3. 如果存在,代表找到了這兩個數,直接回傳 `[]int{m[complement], i}`。 |
80 | | - 4. 如果不存在,將當前數字與索引存入 Map:`m[v] = i`。 |
81 | | -- 預防 Bug:確保先檢查再存入,以避免「同一個元素使用兩次」(例如 target 是 6,當前數字是 3)。 |
| 37 | +1. 初始化一個空的分佈式雜湊表 (Map),用來儲存 `數值 : 下標` 的映射。 |
| 38 | +2. 遍歷陣列 `nums`,對於每個元素 `v` 和其索引 `i`: |
| 39 | + - 計算需要的互補值 `complement = target - v`。 |
| 40 | + - 在 Map 中檢查 `complement` 是否已存在: |
| 41 | + - 如果**存在**:表示找到答案,返回 `[Map[complement], i]`。 |
| 42 | + - 如果**不存在**:將當前數值與索引存入 Map `Map[v] = i`。 |
| 43 | +3. 如果遍歷結束仍未找到(根據題目假設這不會發生),返回空或預設值。 |
| 44 | +- **避免的 Bug**:先檢查再存入,可以自然避免「重複使用同一個元素」的問題(因為 Map 裡只會有之前處理過的元素)。 |
82 | 45 |
|
83 | 46 | ### I – Implement(實際實作,Golang) |
84 | 47 | ```go |
85 | 48 | func twoSum(nums []int, target int) []int { |
86 | | - // 建立 hash map 存儲 {值: 索引} |
| 49 | + // 建立一個 map,key 是數值,value 是對應的索引 |
87 | 50 | m := make(map[int]int) |
88 | 51 |
|
89 | 52 | for i, v := range nums { |
90 | 53 | complement := target - v |
91 | | - // 檢查差值是否已在 map 中 |
| 54 | + // 檢查互補值是否已經在 map 中 |
92 | 55 | if idx, ok := m[complement]; ok { |
93 | 56 | return []int{idx, i} |
94 | 57 | } |
95 | | - // 將當前數字存入 map |
| 58 | + // 如果沒找到,把當前數值存入 map |
96 | 59 | m[v] = i |
97 | 60 | } |
98 | | - return nil |
| 61 | + |
| 62 | + return []int{} |
99 | 63 | } |
100 | 64 | ``` |
101 | 65 |
|
102 | 66 | ### R – Review(檢查與回顧) |
103 | | -- **Dry Run (`nums = [2, 7, 11, 15], target = 9`)**: |
104 | | - - `i=0, v=2`: complement=7。Map 空,存入 `{2: 0}`。 |
105 | | - - `i=1, v=7`: complement=2。Map 中有 2 (索引 0),回傳 `[0, 1]`。正確。 |
106 | | -- **狀態轉換**:Map 動態增長,確保我們只會跟「之前出現過」的數字做比較,完美避開重複使用同一元素。 |
| 67 | +- **Dry Run**:以 `nums = [3, 2, 4], target = 6` 為例: |
| 68 | + 1. `i=0, v=3`:`complement=3`。Map 為空,不匹配。Map 存入 `{3: 0}`。 |
| 69 | + 2. `i=1, v=2`:`complement=4`。Map 只有 `{3: 0}`,不匹配。Map 存入 `{3: 0, 2: 1}`。 |
| 70 | + 3. `i=2, v=4`:`complement=2`。Map 存在鍵 `2`,其索引為 `1`。匹配成功。 |
| 71 | + 4. 返回 `[1, 2]`。 |
| 72 | +- **狀態轉換**:Map 會動態紀錄掃描過的數字,將原本需要二次掃描的查找降為 $O(1)$。 |
107 | 73 |
|
108 | 74 | ### E – Evaluate(總結與評估) |
109 | | -- **Time Complexity**: **O(n)**。只需遍歷陣列一次,Map 的查找與插入均為 O(1)。 |
110 | | -- **Space Complexity**: **O(n)**。最差情況下需要將所有數字存入 Map。 |
111 | | -- **Trade-offs**: 犧牲了空間(O(n))來換取最快的時間(O(n))。 |
| 75 | +- **時間複雜度**:$O(n)$。我們只需遍歷陣列一次,且每次 Map 的存取與查找均為 $O(1)$。 |
| 76 | +- **空間複雜度**:$O(n)$。最壞情況下需要將 $n$ 個元素存入 Map。 |
| 77 | +- **權衡**:使用空間換取時間。這是處理「尋找特定配對」問題最經典的優化方式。 |
112 | 78 |
|
113 | 79 | --- |
114 | 80 |
|
115 | 81 | ## Output 2: 面試官口語回答腳本(精簡可直接說) |
116 | 82 |
|
117 | 83 | ### 1️⃣ 開場:題目理解 |
118 | | -這題是要在陣列中找到兩個數,相加等於給定的 `target`。比較關鍵的要求是每個輸入只有一個正確解,而且同一個元素不能重複用兩次。 |
| 84 | +這題是要在陣列中找到兩個數字,讓它們加起來等於目標值 `target`,並回傳這兩個數字的索引。題目保證一定有一個解,且每個數字不能重複使用。 |
119 | 85 |
|
120 | 86 | ### 2️⃣ 解法選擇說明 |
121 | | -最直觀的方法是兩層迴圈的暴力破解,但時間複雜度是 O(n²)。我會選擇使用 **Hash Map** 來優化,這樣可以把查找的時間降到 O(1),讓整體的效率提升到 **O(n)**。 |
| 87 | +我選擇使用 **Hash Map** 來解這題,因為它能將尋找「互補數字」的時間從 $O(n)$ 降到 $O(1)$。雖然暴力解用雙層迴圈也能做,但 $O(n^2)$ 的效率在數據量大時會太慢。 |
122 | 88 |
|
123 | 89 | ### 3️⃣ 解題策略概覽 |
124 | | -我會遍歷陣列一次,每遇到一個數字,就去算一下「還差多少(complement)」才能達到目標值。如果這個差值已經在 Map 裡面了,就代表找到了;如果還沒出現過,就把現在這個數字存進 Map,留給後面的數字匹配。 |
| 90 | +我會遍歷一次陣列。對於每個數字,我先去計算「還差多少才到 target」,然後檢查這個「差額」是否已經存在 Hash Map 中。如果有,就代表找到了;如果沒有,我就把當前的數字和它的索引存進 Map,繼續往後看。 |
125 | 91 |
|
126 | 92 | ### 4️⃣ 寫程式時會補充的關鍵說明 |
127 | | -在實作時,我會注意**先檢查再存入**。這樣可以確保我們不會誤用同一個索引兩次,例如當 target 是 6 而當前數字是 3 的時候。 |
| 93 | +在實作時有兩個細節要注意: |
| 94 | +1. 我們要「先檢查、再存入」,這樣可以保證我們不會找到自己,滿足題目「不能重複使用同一個元素」的要求。 |
| 95 | +2. 在 Golang 中,使用 `if idx, ok := m[complement]; ok` 可以很優雅地同時完成查找和判斷是否存在。 |
128 | 96 |
|
129 | 97 | ### 5️⃣ 快速 Dry Run 說明 |
130 | | -以 `[2, 7, 11, 15]` 目標 9 為例。第一個數字 2 進去時 Map 是空的,所以把 `2` 存起來。到第二個數字 7 時,差值是 2,這時發現 Map 裡已經有 2 了,就直接回傳它們的索引。 |
| 98 | +假設輸入 `[3, 2, 4]` 目標是 `6`。 |
| 99 | +看到 `3` 時 Map 是空的,存入 `{3:0}`; |
| 100 | +看到 `2` 時去找 `4`,沒找到,存入 `{2:1}`; |
| 101 | +最後看到 `4` 時去找 `2`,在 Map 裡找到了索引 `1`,所以直接回傳 `[1, 2]`。 |
131 | 102 |
|
132 | 103 | ### 6️⃣ 收尾總結 |
133 | | -這個解法在時間上非常高效,是 **O(n)**。雖然空間上因為用了 Map 需要 **O(n)** 的額外空間,但在現代面試中,這通常是時間與空間權衡下的最優解。 |
| 104 | +這個演算法的**時間複雜度是 $O(n)$**,**空間複雜度也是 $O(n)$**。這是在這類尋找配對問題中最優的時間效率。 |
| 105 | + |
| 106 | +--- |
0 commit comments