题目描述:
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 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;
}
可以看到,其基本思路还是没有变化的,只是在优化版的基础上再次进行了优化。
最后,提供另一种解题思路,那就是直接在原链表的基础上进行相加,不需要第三条链表来保存结果,直接将结果保存在原链表上,那么耗时还可以进一步减小,只不过不太符合实际应用,因为改变了传进来的链表结构,有时候用户可不希望传进去两条链表,出来之后就不一样了,所以仅供参考。