小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。
原题:2. 两数相加
简而言之就是将两个包含若干个数字的链表,进行相加,得到新的链表。或者说,把一个加法运算中的两个加数和相加之和都变成数字链表。
解题思路:
首先想到的是将两个链表的各个节点的数字相加,得到的结果就是新链表的对应节点的数字。这其实是最理想的情况,但是还需要考虑以下几个问题。
- 两个数字相加之和达到10就需要考虑进位的问题。
- 如果最后的节点相加的和达到10,结果链表会比两个参数列表都要长。
- 两个链表不一定是一样长的,所以需要在短链表的节点「不沟通」的时候,补充 0。
首先把最开始想到的逻辑代码写出来:
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode curr = pre;
while (l1 != null || l2 != null) {
int x = l1.val;
int y = l2.val;
int sum = x + y;
curr.next = new ListNode(sum);
curr = curr.next;
l1 = l1.next;
l2 = l2.next;
}
return pre.next;
}
}
其中,先创建 pre 作为头节点,这样可以直接在其后跟上新的节点,省去了循环中需要判断第一个节点的情况,最后返回 pre.next 即可。然后,创建 curr 作为当前最后一个节点的指针,新创建的节点既是 curr 的下一个节点。每次将两个参数链表对应位置的值相加作为新节点的值就可以了。
然后,我们考虑两个数字相加需要进位的情况。比如两个数字分别是 7 和 8,它们的和是 15。那么,结果链表相应位置的值应该是 5(也就是15%10),并且,其下一个节点,应该比它对应的两数相加的结果再加 1(也就是15/10)。因此,我们在 while 循环外面,创建一个 carry 变量,用于保存相加结果的十位数字。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode curr = pre;
int carry = 0;
while (l1 != null || l2 != null) {
int x = l1.val;
int y = l2.val;
int sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
curr.next = new ListNode(sum);
curr = curr.next;
l1 = l1.next;
l2 = l2.next;
}
return pre.next;
}
}
这样,计算出两数之和后,把个位的数字放入结果链表的相应节点中,再把十位数字复制给 carry 变量,用于下一个节点两数相加之后再加的数字。(这里 carry 只可能是 0 和 1,而且,两数之和最多只有两位)。
再来考虑第二个问题,最后一个节点结果使两位数的情况,这个问题很好解决,只需要在循环执行完之后,判断一下 carry,如果是 1,则最后再追加一个值为 1 的节点即可。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode curr = pre;
int carry = 0;
while (l1 != null || l2 != null) {
int x = l1.val;
int y = l2.val;
int sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
curr.next = new ListNode(sum);
curr = curr.next;
l1 = l1.next;
l2 = l2.next;
}
if (carry == 1) {
curr.next = new ListNode(carry);
}
return pre.next;
}
}
最后再来解决两个链表不一样长的问题,需要修改两个地方。第一个是循环体开头,取两个链表当前节点的值 x 和 y 的时候,需要判断链表是不是已经遍历完了,也就是 l1 或者 l2 是不是为空,如果是的话,就不能再取它们的值,而是给 x 或者 y 赋值为 0。第二个是循环体的结尾,让 l1 和 l2 指向当前节点的下一个节点的时候,做一下判断,因为两个链表不一样长的话,短的链表已经没有节点了,但是循环还在进行,这时就需要判断 l1 和 l2 不为空的时候,再将其指向下一个节点,否则会报错。
最终代码:
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode pre = new ListNode(0);
ListNode curr = pre;
int carry = 0;
while (l1 != null || l2 != null) {
int x = l1 == null ? 0 : l1.val;
int y = l2 == null ? 0 : l2.val;
int sum = x + y + carry;
carry = sum / 10;
sum = sum % 10;
curr.next = new ListNode(sum);
curr = curr.next;
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
if (carry == 1) {
curr.next = new ListNode(carry);
}
return pre.next;
}
}