LeetCode之两数相加(初级版 + 优化版 + 究极版)

题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:

输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

首先,我们需要考虑这两条链表的长度,有三种情况,我们分别讨论:
情况一:l1.length = l2.length
在这里插入图片描述
如图,这种情况还是很容易理解的

情况二和情况三:l1.lenght > l2.length || l1.length < length
在这里插入图片描述
其次,两数相加必须要考虑进位的问题,主要有两种情况需要考虑,最高位相加后不需要进位,那么只需要考虑低位相的进位,如果最高位相加后需要进位,那么就要额外再最后增加一个节点来保存最高位的进位:
例如:

99 + 1 = 100

接着,将基本情况分析清楚之后我们就可以开始写代码了,接下来的代码中涉及到链表的尾插法,如果没有掌握的读者请先学习之后再来看(大神随意)

  • 初级版
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode ret = new ListNode(0);
        ListNode tail = ret;
        tail.next = null;
        int carry = 0;   //进位标志位,相当于汇编中的CF标志位,如果有进位就置为1,没有进位就置为0;
        while (l1 != null || l2 != null) {   //只要两条链表有任意一条不为空,就继续遍历
            if (l1 != null && l2 != null) {  //可以理解为情况一
                int sum = l1.val + l2.val + carry;
                if (sum > 9) {
                    sum = sum % 10;
                    carry = 1;
                } else {
                    carry = 0;  //注意,如果没有进位一定要置为0,否则可能 产生意向不到的结果
                }
                ListNode node = new ListNode(sum);  //尾插法插入结点
                tail.next = node;
                tail = node;
                l1 = l1.next;
                l2 = l2.next;
            }else if (l1 == null && l2 != null){  //此处可以理解为情况二或者情况三
                int  sum1 = l2.val + carry;
                if (sum1 > 9){
                    sum1 %= sum1;
                    carry = 1;
                }else {
                    carry = 0;
                }

                tail.next = new ListNode(sum1);
                tail = tail.next;
                l2 = l2.next;
            }else {
                int  sum2 = l1.val + carry;
                if (sum2 > 9){
                    sum2 %= sum2;
                    carry = 1;
                }else {
                    carry = 0;
                }
                tail.next = new ListNode(sum2);
                tail = tail.next;
                l1 = l1.next;
            }
        }
        if (carry > 0) {  //这里需要注意,当循环结束以后如果carry>0表示最高位有进位,需要再创建一个新的结点保存进位  
            ListNode preNode = new ListNode(carry);
            tail.next = preNode;
            preNode = tail;
        }
        return ret.next;
    }

提交通过截图:
在这里插入图片描述
可以看到,耗时为3毫秒,内存消耗为41.1MB,但是作为当代优秀大学生,只是通过怎么可以呢,而且只是击败了61%的用户,下面我们来看看哪些地方是可以优化的:
在这里插入图片描述在这里插入图片描述在这里插入图片描述我们来看这三段代码,是不是有重复的地方呢?直觉告诉我们,这些重复的地方都是可以优化的,这三段代码只要干了一件事情,那就是计算sum,有人肯定已经想到了,没错,优化之后的代码如下:

  • 优化版
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode p1 = l1;
        ListNode p2 = l2;
        ListNode ret = new ListNode(0);
        ListNode tail = ret;
        int carry = 0;
        int sum = 0;
        while (p1 != null || p2 != null){
            int v1 = (p1 != null) ? p1.val : 0;
            int v2 = (p2 != null) ? p2.val : 0;
            sum = v1 + v2 + carry;
            carry = sum / 10;

            tail.next = new ListNode(sum % 10);
            tail = tail.next;

            p1 = p1 == null ? null : p1.next;
            p2 = p2 == null ? null : p2.next;
        }

        if (carry > 0){
            tail.next = new ListNode(1);
        }
        return  ret.next;
    }

我们再提交看看:
在这里插入图片描述可以看到,耗时为2ms,但是,竟然还有一位耗时1ms的仁兄,真的是猛人,我们来看看这位仁兄是怎么写的吧:

  • 究极版,非原创,参考自leetcode耗时1ms的代码
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
        ListNode dummy = new ListNode(0);
        ListNode cur = dummy;
        int carry = 0;
        while (l1 != null || l2 != null) {
            int sum = carry;
            if (l1 != null) sum += l1.val;
            if (l2 != null) sum += l2.val;
            cur.next = new ListNode(sum % 10);
            carry = sum / 10;
            l1 = l1 == null ? null : l1.next;
            l2 = l2 == null ? null : l2.next;
            cur = cur.next;
        }
        if (carry != 0) {
            cur.next = new ListNode(carry);
        }
        return dummy.next;
    }

可以看到,其基本思路还是没有变化的,只是在优化版的基础上再次进行了优化。

最后,提供另一种解题思路,那就是直接在原链表的基础上进行相加,不需要第三条链表来保存结果,直接将结果保存在原链表上,那么耗时还可以进一步减小,只不过不太符合实际应用,因为改变了传进来的链表结构,有时候用户可不希望传进去两条链表,出来之后就不一样了,所以仅供参考。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
根据提供的引用内容,Leetcode 2 "两数相加"是一个涉及链表的问题。该问题给定了两个非负整数,每个整数的每一位都是按照逆序的方式存储在链表中。我们需要将这两个链表相加,并返回一个新的链表作为结果。 具体解题思路可以使用迭代法或递归法来解决。迭代法的伪代码如下所示: ``` 初始化一个哑节点 dummy 和一个进位 carry,同时把两个链表的头节点分别赋值给 p 和 q 遍历链表,直到 p 和 q 都为 None 计算当前的和 sum 为 p.val + q.val + carry 计算当前的进位 carry 为 sum // 10 创建一个新节点 node,节点的值为 sum % 10 把新节点连接到结果链表的尾部 更新 p 和 q 分别为 p.next 和 q.next 如果最后还有进位 carry,则创建一个新节点 node,节点的值为 carry,并连接到结果链表的尾部 返回结果链表的头节点 dummy.next ``` 递归法的伪代码如下所示: ``` 定义一个辅助函数 addTwoNumbersHelper,输入为两个链表的头节点 p 和 q,以及进位 carry 如果 p 和 q 都为 None 且 进位 carry 为 0,则返回 None 计算当前的和 sum 为 p.val + q.val + carry 计算当前的进位 carry 为 sum // 10 创建一个新节点 node,节点的值为 sum % 10 设置新节点的下一个节点为递归调用 addTwoNumbersHelper(p.next, q.next, carry) 返回新节点 返回 addTwoNumbersHelper(p, q, 0) 的结果 以上是解决 Leetcode 2 "两数相加"问题的两种方法。如果你还有其他相关问题,请
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值