给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0]
输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]
提示:
每个链表中的节点数在范围 [1, 100] 内
0 <= Node.val <= 9
题目数据保证列表表示的数字不含前导零
解题思路
方法一:一次遍历依次累加法
思路:同时遍历l1,l2,每个节点相加,组成一个新的节点。作为链表l3的下一个节点。如果大于9就进1。下两个节点相加时要把进1加上。一直到其中一个链表遍历完。
这个时候会有三种情况,要么全都遍历完了,这时判断是否进1,如果进1只需生成一个新的节点值为1,加到l3结尾。如果l1没遍历完,如果没有进位,l3直接接上l1剩下的部分。如果有进位,要加上进位。此时还可以产生进位。一直循环到进位为0或者l1遍历完。如果遍历完,进位不为0,还需在l1的结尾追加一个值为1的节点。c3加上l1的剩余部分。如果l2没有遍历完。同理l1.
做法:
1、求l1,l2第一个节点的和。如果大于等于10进位add=1,否则为0.生成新的节点,值为sum%10,作为l3的第一个节点。
2、对l1,l2剩下的节点进行遍历,一直到12某个遍历完。
3、对便利的节点进行相加,还要加上进位(0或1),更新新的进位。创建新的节点,值为和%10,追加到l3的尾部
4、对剩下的元素进行处理。这时会有三种情况
4.1、l1l2全都遍历完了。只需要判断是否有进位,如果有直接生成新元素追加到l3结尾。否则不管他
4.2、l1没有遍历完。将l3的结尾拼接上l1的剩下部分。并对剩下部分进行更新。如果没有进位,直接追加即可,不需要更新。如果有进位,需要下一个元素加上进位1.此时可能产生新的进位。所以用一个循环进行处理,当没有进位或者l1遍历完时结束。如果l1遍历完了,判断是否还有进位。如果有,生成新元素追加到l1尾部。
4.3、l2没有遍历完。同理l1.
时间复杂度:O(n),n为链表1,2最大长度。空间复杂度:O(n+m),nm分别为l1l2的长度
代码
/**
* 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 static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode c1 = l1, c2 = l2;
int sum = c1.val + c2.val; //l12第一个元素相加
int add = sum >= 10 ? 1 : 0; //求得进位
ListNode l3 = new ListNode(sum%10); //生成新元素,作为l3的第一个节点
ListNode c3 = l3;
c1 = c1.next;
c2 = c2.next;
//对l1,l2进行遍历,对l3进行追加
while (c1 != null && c2 != null) {
sum = c1.val + c2.val + add; //将l1l2的遍历到的元素相加,加上进位(0或1)
add = sum >= 10 ? 1 : 0; //更新新的进位
ListNode node = new ListNode(sum%10); //生成新元素,追加到l3
//l1,2,3的遍历节点后移
c3.next = node;
c1 = c1.next;
c2 = c2.next;
c3 = c3.next;
}
//判断三种情况
if (c1 != null) { //情况1:l1没有遍历完
ListNode pre = c3; //保存c1的前驱节点
c3.next = c1; //c3的尾部追加l1的剩余部分
while (c1 != null && add != 0) { //当进位为0或者l1遍历完结束
ListNode next = c1.next; //保存当前节点的下一个节点
sum = c1.val + add; //计算当前节点和进位的和
add = sum >= 10 ? 1 : 0; //更新进位
c1 = new ListNode(sum%10); //生成新节点
c1.next = next; //新节点拼接上剩余节点
pre.next = c1; //当前节点的前驱拼接新节点。上两步和这步相当于新节点替换老节点c1.
pre = c1; //更新当前节点前驱
c1 = c1.next; //更新节点下移一位
}
//最后还要判断进位是否是1,因为可能l1遍历完了,但是进位还是1
if (add == 1) {
//生成新节点追加到l1的尾部
ListNode node = new ListNode(add);
pre.next = node;
}
} else if (c2 != null) { //情况2:l2没有遍历完。和上一种情况类似
ListNode pre = c3;
c3.next = c2;
while (c2 != null && add != 0) {
ListNode next = c2.next;
sum = c2.val + add;
add = sum >= 10 ? 1 : 0;
c2 = new ListNode(sum%10);
c2.next = next;
pre.next = c2;
pre = c2;
c2 = c2.next;
}
if (add == 1) {
ListNode node = new ListNode(add);
pre.next = node;
}
} else { //情况3:l1,2全都遍历完了。
if (add == 1) { //只需判断进位是否是1,是1的话生成新的节点添加到l3尾部。
ListNode node = new ListNode(add);
c3.next = node;
}
}
//返回链表3的头节点。
return l3;
}
}
方法二:
一次遍历短接点追加值0的节点,直到两链表一样长
思路:上一个方法的缺点很明显,就是可能链表一长一短,需要对剩下的链表进行额外的处理。耗费大量的资源。可不可以对这个缺点进行改进呢?可以,只需保持两个链表一样长即可。对短的部分进行追加值为0的节点。当全都遍历完了,最后只需判断是否有进位1,有的话追加一个值为1的新节点即可。
时间复杂度:O(n),n为l1,2最大长度。空间复杂度:O(n+m),nm分别为l1l2的长度
代码
public static ListNode addTwoNumbers2(ListNode l1, ListNode l2) {
ListNode c1 = l1, c2 = l2;
int sum = c1.val + c2.val; //计算l12第一个节点的和
int add = sum >= 10 ? 1 : 0; //更新进位
ListNode l3 = new ListNode(sum%10); //生成新节点作为l3的第一个节点
ListNode c3 = l3;
c1 = c1.next;
c2 = c2.next;
ListNode p1=l1,p2=l2; //保存遍历当前节点的前驱节点。追加节点需要用
while (!(c1 == null && c2 == null)) { //当l1,2同时为空时结束。
if (c1 == null) { //如果l1遍历完了,此时l2还没完,追加值为0的新节点
c1 = new ListNode(0);
p1.next = c1;
}
if (c2 == null) { //和上面同理
c2 = new ListNode(0);
p2.next = c2;
}
//计算当前节点和进位的和
sum = c1.val + c2.val + add;
add = sum >= 10 ? 1 : 0; //更新进位
ListNode node = new ListNode(sum%10); //生成新节点。值为sum%10
c3.next = node; //将新节点追加到l3的尾部
//c1,2,3全都往后移一位
c1 = c1.next;
c2 = c2.next;
c3 = c3.next;
//更新当前节点的前驱节点
p1 = p1.next;
p2 = p2.next;
}
//最后只需判断是否有进位
if (add == 1) {
ListNode node = new ListNode(add);
c3.next = node;
}
//返回l3的头结点
return l3;
}