首先,通过 listLen(ListNode l)
方法分别计算出两个链表 l1
和 l2
的长度。然后,它根据长度比较,将较长的链表赋给 l
(long),将较短的链表赋给 s
(short)。这么做是为了后续的加法操作能直接在较长的链表上进行修改,而不需要创建新的节点来存储结果(除了可能出现的最后一位进位)。
然后开始遍历链表,分为下面3个阶段:
- 同时遍历长短链表:计算当前两个节点的值以及上一位的进位 carry 的和:
curNum = l.val + s.val + carry
。计算新的进位:carry = curNum / 10
。将当前位的结果(个位数)存回较长的链表节点:l.val = curNum % 10
。更新 last 节点,以便在循环结束后能够处理可能的最后进位。将 l 和 s 分别移动到下一个节点。这个循环会一直运行,直到较短的链表 s 遍历完毕。 - 处理长链表(此时短链表已经遍历完):当短链表
s
遍历结束后,长链表l
可能还有剩余的节点。使用第二个while
循环来处理这些剩余节点。它只将每个节点的值与carry
相加,并更新进位和当前节点的值。这个循环确保了即使两个链表的长度不同,加法操作也能正确地从低位(链表头部)一直延伸到高位(链表尾部)。 - 处理最后的进位:如果两个链表的所有节点都处理完毕后,
carry
的值仍然不为零(例如,5 -> 9
和5
相加,结果是0 -> 0 -> 1
),这意味着需要新增一个节点来存储这个进位。通过if (carry != 0)
判断是否存在进位,如果存在,则在上一个处理的节点last
的末尾添加一个新的节点,其值为1
。
最后,代码返回 head
,即较长链表的头节点。因为所有的加法操作都是在较长链表上进行的,这个链表现在已经包含了最终的和。
这种原地修改的思路避免了创建大量新节点的开销,是解决这类链表问题的常见策略之一。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 分出链表哪个长,哪个短
int len1 = listLen(l1);
int len2 = listLen(l2);
ListNode l = len1 >= len2 ? l1 : l2;
ListNode head = l;
ListNode s = head == l1 ? l2 : l1;
ListNode last = l;
int curNum = 0;
int carry = 0;
// 两个都还有
while (l != null && s != null) {
curNum = l.val + s.val + carry;
carry = curNum / 10;
l.val = curNum % 10;
last = l;
l = l.next;
s = s.next;
}
// 长的还有
while (l != null) {
curNum = l.val + carry;
carry = curNum / 10;
l.val = curNum % 10;
last = l;
l = l.next;
}
// carry还有
if (carry != 0) {
last.next = new ListNode(1);
}
return head;
}
public int listLen(ListNode l) {
int ans = 0;
while (l != null) {
ans++;
l = l.next;
}
return ans;
}
}