2. 两数相加 Add Two Numbers

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值