|
| 1 | +# 2. Add Two Numbers |
| 2 | + |
| 3 | +You are given two non-empty linked lists representing two non-negative integers. |
| 4 | +The digits are stored in reverse order, and each of their nodes contains a single digit. Add the two numbers and return the sum as a linked list. |
| 5 | + |
| 6 | +You may assume the two numbers do not contain any leading zero, except the number 0 itself. |
| 7 | + |
| 8 | +**Constraints:** |
| 9 | + |
| 10 | +- The number of nodes in each linked list is in the range `[1, 100]`. |
| 11 | +- `0 <= Node.val <= 9` |
| 12 | +- It is guaranteed that the list represents a number that does not have leading zeros. |
| 13 | + |
| 14 | +## 基礎思路 |
| 15 | + |
| 16 | +本題要求將兩個反向儲存的數字(以鏈結串列表示)加總,並回傳結果同樣以反向儲存的鏈結串列表示。 |
| 17 | +這就像是在紙上做「直式加法」,從個位數開始逐位計算。 |
| 18 | + |
| 19 | +為了完成這件事,我們可以採用以下策略: |
| 20 | + |
| 21 | +- 每次同時讀取兩個串列當前位的數字進行相加,若某一方已走到底則補 0。 |
| 22 | +- 使用一個變數 `carry` 紀錄每次相加是否產生進位。 |
| 23 | +- 將每位加總結果建立成新節點,依序串接到結果串列後方。 |
| 24 | +- 為了簡化操作,使用一個「虛擬頭節點」來建立結果串列,避免特殊處理第一個節點。 |
| 25 | +- 加總結束後,若仍有進位,須額外補上一節點。 |
| 26 | + |
| 27 | +這些操作都能在一次遍歷中完成,空間上只需要建一條新的串列,額外變數使用極少,因此效率也很高。 |
| 28 | + |
| 29 | +## 解題步驟 |
| 30 | + |
| 31 | +### Step 1:處理空串列的邊界情況 |
| 32 | + |
| 33 | +若其中一條輸入串列為空,則結果等於另一條串列。 |
| 34 | + |
| 35 | +```typescript |
| 36 | +// 處理其中一條串列為空的邊界情況 |
| 37 | +if (l1 === null) { |
| 38 | + return l2; |
| 39 | +} |
| 40 | +if (l2 === null) { |
| 41 | + return l1; |
| 42 | +} |
| 43 | +``` |
| 44 | + |
| 45 | +### Step 2:建立虛擬頭節點與初始化尾指標 |
| 46 | + |
| 47 | +使用 dummy head 方便後續統一插入節點的邏輯,避免判斷是否為第一個節點。 |
| 48 | + |
| 49 | +```typescript |
| 50 | +// 以虛擬頭節點簡化結果串列的建立,並維護尾指標以便追加新節點 |
| 51 | +const dummyHeadNode = new ListNode(0); |
| 52 | +let resultTailNode = dummyHeadNode; |
| 53 | +``` |
| 54 | + |
| 55 | +### Step 3:初始化指標與進位變數 |
| 56 | + |
| 57 | +設置用來遍歷兩條輸入串列的指標,並初始化進位變數為 0。 |
| 58 | + |
| 59 | +```typescript |
| 60 | +// 初始化走訪指標與進位(處理位數進位) |
| 61 | +let list1Pointer: ListNode | null = l1; |
| 62 | +let list2Pointer: ListNode | null = l2; |
| 63 | +let carryOver = 0; |
| 64 | +``` |
| 65 | + |
| 66 | +### Step 4:遍歷兩條串列,逐位計算並建立新節點 |
| 67 | + |
| 68 | +此步驟進行主邏輯:同步走訪兩串列,計算每一位的總和與進位,並建立對應節點加入結果串列。 |
| 69 | + |
| 70 | +```typescript |
| 71 | +// 同步走訪兩串列直到所有位元處理完畢 |
| 72 | +while (list1Pointer !== null || list2Pointer !== null) { |
| 73 | + // 取得當前位數字(較短的串列視為 0) |
| 74 | + const digitFromList1 = list1Pointer ? list1Pointer.val : 0; |
| 75 | + const digitFromList2 = list2Pointer ? list2Pointer.val : 0; |
| 76 | + |
| 77 | + // 計算兩位數字加上前一次進位的總和 |
| 78 | + const sumOfDigits = digitFromList1 + digitFromList2 + carryOver; |
| 79 | + |
| 80 | + // 以新節點記錄本位數值,並更新進位 |
| 81 | + if (sumOfDigits >= 10) { |
| 82 | + resultTailNode.next = new ListNode(sumOfDigits - 10); |
| 83 | + carryOver = 1; |
| 84 | + } else { |
| 85 | + resultTailNode.next = new ListNode(sumOfDigits); |
| 86 | + carryOver = 0; |
| 87 | + } |
| 88 | + |
| 89 | + // 推進尾指標與輸入串列的走訪指標 |
| 90 | + resultTailNode = resultTailNode.next!; |
| 91 | + if (list1Pointer !== null) { |
| 92 | + list1Pointer = list1Pointer.next; |
| 93 | + } |
| 94 | + if (list2Pointer !== null) { |
| 95 | + list2Pointer = list2Pointer.next; |
| 96 | + } |
| 97 | +} |
| 98 | +``` |
| 99 | + |
| 100 | +### Step 5:處理最終進位(若有) |
| 101 | + |
| 102 | +若最後仍有進位,代表需補上一個進位節點。 |
| 103 | + |
| 104 | +```typescript |
| 105 | +// 若處理完後仍有進位,將其以新節點補到尾端 |
| 106 | +if (carryOver !== 0) { |
| 107 | + resultTailNode.next = new ListNode(carryOver); |
| 108 | +} |
| 109 | +``` |
| 110 | + |
| 111 | +### Step 6:回傳實際結果的頭節點 |
| 112 | + |
| 113 | +跳過 dummy 頭節點,回傳真正的加總結果。 |
| 114 | + |
| 115 | +```typescript |
| 116 | +// 回傳實際結果頭節點(跳過虛擬頭) |
| 117 | +return dummyHeadNode.next; |
| 118 | +``` |
| 119 | + |
| 120 | +## 時間複雜度 |
| 121 | + |
| 122 | +- 需要同時走訪兩條鏈結串列,遍歷過程中每個節點只處理一次。 |
| 123 | +- 每次節點操作(加總、建立節點、串接、指標移動)皆為常數時間。 |
| 124 | +- 總時間複雜度為 $O(n)$,其中 $n$ 為兩條輸入串列中較長者的節點數。 |
| 125 | + |
| 126 | +> $O(n)$ |
| 127 | +
|
| 128 | +## 空間複雜度 |
| 129 | + |
| 130 | +- 在整個加總過程中僅使用了固定數量的指標與變數,因此額外使用的空間為常數。 |
| 131 | +- 此外建立了一條新的結果鏈結串列,其長度最多為兩條輸入串列長度中較大的那一個加 1。 |
| 132 | +- 總空間複雜度為 $O(n)$,其中 $n$ 為輸出鏈結串列的節點數。 |
| 133 | + |
| 134 | +> $O(n)$ |
0 commit comments