2. 两数相加
给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
思路一:模拟大数加法
模拟大数加法,先处理都不为空的结点,然后分别处理另一个更长的链表中剩下的结点,最后还要考虑fn的值是否需要再创建一个结点。
1 class Solution {
2 public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
3 // 模拟大数加法
4 ListNode head = new ListNode(); // 增加一个冗余的头结点
5 ListNode cur = head;
6
7 int fn = 0; // 表示进位
8 int temp = 0; // 表示两个个位数相加的临时结果
9 ListNode p1 = l1, p2 = l2;
10 while(p1 != null && p2 != null){
11 temp = p1.val + p2.val + fn;
12 cur.next = new ListNode(temp % 10); // 只存储余数
13 fn = temp / 10; // 更新进位
14 cur = cur.next;
15 p1 = p1.next;
16 p2 = p2.next;
17 }
18 // 处理p1多余的结点
19 while(p1 != null){
20 temp = p1.val + fn;
21 cur.next = new ListNode(temp % 10); // 只存储余数
22 fn = temp / 10; // 更新进位
23 cur = cur.next;
24 p1 = p1.next;
25 }
26 // 处理p2多余的结点
27 while(p2 != null){
28 temp = p2.val + fn;
29 cur.next = new ListNode(temp % 10); // 只存储余数
30 fn = temp / 10; // 更新进位
31 cur = cur.next;
32 p2 = p2.next;
33 }
34
35 if(fn != 0){ // 处理最后的进位
36 cur.next = new ListNode(fn); // 只存储余数
37 }
38 return head.next;
39 }
40 }
leetcode 执行用时:2 ms > 99.92%, 内存消耗:39 MB > 46.86%
复杂度分析:
时间复杂度:O(max(n1, n2))。n1和 n2 分别为链表的长度,虽然代码实现中有3个循环,但是通过实现逻辑可以看出所有迭代次数之和刚好是两个链表长度大的那条的结点个数,即max(n1, n2)。
空间复杂度:O(max(n1, n2))。这是用来存储结果的链表的长度,如果不考虑这部分空间复杂度,则空间复杂度为O(1)。
思路二:思路一的改进版
思路和思路一一样,同样是利用了大数加法的思想,但是可以看出思路一的代码太长,存在冗余代码,所以考虑将三个循环放在同一个循环中。思路一之所以要分成3个循环,是因为担心其中一个链表的长度大于另一个链表长度,这样遍历到短链表的结尾时长链表还没有遍历完,如果继续遍历则会导致短链表空指针异常。所以改进就是取值之前先判断结点是否为空,如果为空,则用0代替,因为0不会改变结果。当然指针后移的时候也要判断结点是否为空,如果不为空才能后移。
1 class Solution {
2 public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
3 // 模拟大数加法
4 ListNode head = new ListNode(); // 增加一个冗余的头结点
5 ListNode cur = head;
6
7 int fn = 0; // 表示进位
8 int temp = 0; // 表示两个个位数相加的临时结果
9 ListNode p1 = l1, p2 = l2;
10 while(p1 != null || p2 != null){
11 int x = (p1 != null ? p1.val : 0); // 取值之前先判断结点是否为空
12 int y = (p2 != null ? p2.val : 0);
13 temp = x + y + fn;
14 cur.next = new ListNode(temp % 10); // 只存储余数
15 fn = temp / 10; // 更新进位
16 cur = cur.next;
17 if(p1 != null){ // 后移之前也需判断结点是否为空
18 p1 = p1.next;
19 }
20 if(p2 != null){
21 p2 = p2.next;
22 }
23 }
24
25 if(fn != 0){ // 处理最后的进位
26 cur.next = new ListNode(fn); // 只存储余数
27 }
28 return head.next;
29 }
30 }
复杂度分析:
时间复杂度:O(max(n1, n2))。n1和 n2 分别为链表的长度,可以看出所有迭代次数之和刚好是两个链表长度大的那条的结点个数,即max(n1, n2)。
空间复杂度:O(max(n1, n2))。这是用来存储结果的链表的长度,如果不考虑这部分空间复杂度,则空间复杂度为O(1)。