|
| 1 | +--- |
| 2 | + |
| 3 | +# Solution - UMPIRE |
| 4 | + |
| 5 | +## Output 1: UMPIRE 解題(完整思考版) |
| 6 | + |
| 7 | +### U – Understand(理解題目) |
| 8 | +- **題目描述**:給予兩個非空的鏈結串列,分別代表兩個非負整數。數字是以「逆序」儲存的,意即鏈結串列的第一個節點代表個位數。請將這兩個數字相加,並以鏈結串列的形式回傳總和。 |
| 9 | +- **關鍵限制**: |
| 10 | + - 每個節點只包含一個數字(0-9)。 |
| 11 | + - 除非是數字 0 本身,否則這兩個數字都不會有前導零。 |
| 12 | + - 兩個鏈結串列的長度可能不同。 |
| 13 | +- **澄清與細節**: |
| 14 | + - 題目保證非空,所以不需處理空指標作為輸入的情況。 |
| 15 | + - 需要處理進位(Carry)。 |
| 16 | +- **測試案例定義**: |
| 17 | + - **Happy Path**: |
| 18 | + - $L1: [2, 4, 3]$ (342), $L2: [5, 6, 4]$ (465) $\rightarrow$ 結果: $[7, 0, 8]$ (807) |
| 19 | + - **Edge Cases**: |
| 20 | + - **長度不同**: $L1: [9, 9, 9, 9, 9, 9, 9]$, $L2: [9, 9, 9, 9] \rightarrow$ 需要處理不同步的結束。 |
| 21 | + - **最後進位**: $L1: [5]$, $L2: [5] \rightarrow$ 結果: $[0, 1]$,最後多出一個節點。 |
| 22 | + - **其中一個為零**: $L1: [0]$, $L2: [7, 3] \rightarrow$ 結果: $[7, 3]$。 |
| 23 | + |
| 24 | +### M – Match(匹配知識) |
| 25 | +- **主要演算法/資料結構**:鏈結串列遍歷(Linked List Traversal)與基礎算術(Elementary Math)。 |
| 26 | +- **為什麼適合**: |
| 27 | + - 因為數字已經是逆序儲存,這非常符合我們手寫加法的習慣(從個位數開始加),我們只需要同時遍歷兩個串列即可。 |
| 28 | +- **為什麼其他解法不理想**: |
| 29 | + - 若先將鏈結串列轉為大整數(BigInt)再相加後轉回串列,雖然可行,但會消耗額外的空間與轉換時間,且無法處理超過 64 位元整數範圍的情況(若題目沒有明確限制範圍)。直接在鏈結串列上操作是最直觀且有效率的。 |
| 30 | + |
| 31 | +### P – Plan(制定計畫) |
| 32 | +- **解題流程**: |
| 33 | + 1. 初始化一個 `dummyHead` 節點來作為結果串列的起始,以及一個指標 `curr` 指向它。 |
| 34 | + 2. 初始化 `carry` 為 0。 |
| 35 | + 3. 同時遍歷 `l1` 和 `l2`,只要 `l1 != nil` 或 `l2 != nil` 或 `carry != 0` 就繼續迴圈: |
| 36 | + - 取得 `l1` 的值(若已結束則為 0)。 |
| 37 | + - 取得 `l2` 的值(若已結束則為 0)。 |
| 38 | + - 計算 `sum = val1 + val2 + carry`。 |
| 39 | + - 更新 `carry = sum / 10`。 |
| 40 | + - 創建新節點其值為 `sum % 10`,掛在 `curr.Next`。 |
| 41 | + - 將 `curr`、`l1`、`l2` 分別移至下一個位置。 |
| 42 | + 4. 回傳 `dummyHead.Next`。 |
| 43 | +- **資料流與狀態變化**: |
| 44 | + - 每次疊代都會處理一位數。 |
| 45 | + - `carry` 狀態會在下一次疊代中被累加。 |
| 46 | +- **預防 Bug**: |
| 47 | + - 注意迴圈結束條件,必須包含 `carry != 0`,否則最後一位進位會被遺漏(例如 5+5=10)。 |
| 48 | + - 檢查指標是否為 `nil` 才能存取 `Val`。 |
| 49 | + |
| 50 | +### I – Implement(實際實作,Golang) |
| 51 | +```go |
| 52 | +/** |
| 53 | + * Definition for singly-linked list. |
| 54 | + * type ListNode struct { |
| 55 | + * Val int |
| 56 | + * Next *ListNode |
| 57 | + * } |
| 58 | + */ |
| 59 | +func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode { |
| 60 | + dummyHead := &ListNode{Val: 0} |
| 61 | + curr := dummyHead |
| 62 | + carry := 0 |
| 63 | +
|
| 64 | + for l1 != nil || l2 != nil || carry != 0 { |
| 65 | + val1, val2 := 0, 0 |
| 66 | + if l1 != nil { |
| 67 | + val1 = l1.Val |
| 68 | + l1 = l1.Next |
| 69 | + } |
| 70 | + if l2 != nil { |
| 71 | + val2 = l2.Val |
| 72 | + l2 = l2.Next |
| 73 | + } |
| 74 | +
|
| 75 | + sum := val1 + val2 + carry |
| 76 | + carry = sum / 10 |
| 77 | + curr.Next = &ListNode{Val: sum % 10} |
| 78 | + curr = curr.Next |
| 79 | + } |
| 80 | +
|
| 81 | + return dummyHead.Next |
| 82 | +} |
| 83 | +``` |
| 84 | + |
| 85 | +### R – Review(檢查與回顧) |
| 86 | +- **Dry Run**: |
| 87 | + - 輸入:$L1: [2, 4, 3], L2: [5, 6, 4]$ |
| 88 | + - 1st: $2+5+0 = 7, carry=0, Result: [7]$ |
| 89 | + - 2nd: $4+6+0 = 10, carry=1, Result: [7, 0]$ |
| 90 | + - 3rd: $3+4+1 = 8, carry=0, Result: [7, 0, 8]$ |
| 91 | + - 結束。回傳 $[7, 0, 8]$。 |
| 92 | +- **關鍵狀態轉移**:`carry` 正確地從十位數轉移到下一位的個位數。 |
| 93 | +- **邊界確認**:如果輸入是 $L1:[9], L2:[1]$,迴圈會跑兩次,第二次處理 `carry=1`,產出 $[0, 1]$,正確。 |
| 94 | + |
| 95 | +### E – Evaluate(總結與評估) |
| 96 | +- **時間複雜度**: $O(\max(M, N))$,其中 $M$ 和 $N$ 分別為兩個串列的長度。我們只需遍歷最長的那一個一次。 |
| 97 | +- **空間複雜度**: $O(\max(M, N))$,回傳的結果串列長度最多為 $\max(M, N) + 1$。若不計入回傳空間,則為 $O(1)$。 |
| 98 | +- **權衡與優化**:這個方法已經是時間與空間的最優解。唯一的優化可能是原位(In-place)修改,但因為相加後長度可能增加,且通常不建議修改輸入參數,所以使用新串列是標準做法。 |
| 99 | + |
| 100 | +--- |
| 101 | + |
| 102 | +## Output 2: 面試官口語回答腳本(精簡可直接說) |
| 103 | + |
| 104 | +### 1️⃣ 開場:題目理解 |
| 105 | +這題要求我們將兩個以逆序鏈結串列表示的整數相加,並回傳同樣是逆序的結果串列。核心在於如何處理兩串列長度不等的情況,以及數值相加後的進位處理。 |
| 106 | + |
| 107 | +### 2️⃣ 解法選擇說明 |
| 108 | +我會採用**同時遍歷(Simultaneous Traversal)**的方法。因為題目給的數字已經是從個位數開始的逆序排列,這正好符合手算加法從右到左的邏輯。這種做法的時間複雜度是線性 $O(\max(M, N))$,是最直觀且高效的解法。 |
| 109 | + |
| 110 | +### 3️⃣ 解題策略概覽 |
| 111 | +我會建立一個 Dummy Head 來簡化鏈結串列的操作。接著使用一個 `while` 迴圈同時遍歷兩個串列,並維護一個 `carry` 變數來存儲進位值。在每一次疊代中,我把兩個節點的值與當前的進位相加,取餘數作為新節點的值,取商數作為下一次的進位。這個迴圈會持續運行,直到兩個串列都走完「且」進位值為 0。 |
| 112 | + |
| 113 | +### 4️⃣ 寫程式時會補充的關鍵說明 |
| 114 | +* **Dummy Head 的使用**:這可以讓我們在建立結果串列時,不需要特別判斷第一個節點是否為空,代碼會更簡潔。 |
| 115 | +* **防呆判斷**:在取值時,一定要先檢查指標是否為 `nil`,如果其中一個串列比較短,我們就補 0 進行運算。 |
| 116 | +* **最後進位處理**:迴圈的條件必須包含 `carry != 0`,確保像是 $99 + 1$ 這種最後產生的進位能被正確補在串列末端。 |
| 117 | + |
| 118 | +### 5️⃣ 快速 Dry Run 說明 |
| 119 | +以 $[2, 4, 3]$ 加 $[5, 6, 4]$ 為例: |
| 120 | +第一位 $2+5=7$,無進位。 |
| 121 | +第二位 $4+6=10$,寫 $0$ 進位 $1$。 |
| 122 | +第三位 $3+4$ 再加上剛才的進位 $1$ 等於 $8$。 |
| 123 | +最後結果就是 $[7, 0, 8]$。 |
| 124 | + |
| 125 | +### 6️⃣ 收尾總結 |
| 126 | +這套演算法的**時間複雜度是 $O(\max(M, N))$**,**空間複雜度也是 $O(\max(M, N))$**(用於儲存結果)。這種解法穩定且能處理極大的數值,因為它完全避開了整數溢出的限制。 |
0 commit comments