题目
标题和出处
标题:两数相加
出处:2. 两数相加
难度
3 级
题目描述
要求
给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个结点只能存储一位数字。将两个数相加,并返回一个表示和的链表。
你可以假设除了数字 0 \texttt{0} 0 之外,这两个数都不会以 0 \texttt{0} 0 开头。
示例
示例 1:
输入:
l1
=
[2,4,3],
l2
=
[5,6,4]
\texttt{l1 = [2,4,3], l2 = [5,6,4]}
l1 = [2,4,3], l2 = [5,6,4]
输出:
[7,0,8]
\texttt{[7,0,8]}
[7,0,8]
解释:
342
+
465
=
807
\texttt{342 + 465 = 807}
342 + 465 = 807
示例 2:
输入:
l1
=
[0],
l2
=
[0]
\texttt{l1 = [0], l2 = [0]}
l1 = [0], l2 = [0]
输出:
[0]
\texttt{[0]}
[0]
示例 3:
输入:
l1
=
[9,9,9,9,9,9,9],
l2
=
[9,9,9,9]
\texttt{l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]}
l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:
[8,9,9,9,0,0,0,1]
\texttt{[8,9,9,9,0,0,0,1]}
[8,9,9,9,0,0,0,1]
数据范围
- 每个链表中的结点数在范围 [1, 100] \texttt{[1, 100]} [1, 100] 内
- 0 ≤ Node.val ≤ 9 \texttt{0} \le \texttt{Node.val} \le \texttt{9} 0≤Node.val≤9
- 题目数据保证列表表示的数字不含前导零
解法
思路和算法
由于给定的两个链表中,每位数字按照从低到高的顺序存储,因此同时从两个链表的头结点开始依次遍历两个链表,在两个链表中遍历到的结点对应的数位是相同的,即都是个位、都是十位、都是百位等。相同数位的数字可以直接相加,相加过程中会产生进位。
同时遍历两个链表,遍历过程可分为三个阶段:两个链表都有结点、只有一个链表有结点、只有进位部分。
第一阶段,两个链表都有结点。记两个链表中遍历到的结点分别为 node 1 \textit{node}_1 node1 和 node 2 \textit{node}_2 node2,进位值为 carry \textit{carry} carry,则两个结点对应的和结点的值为 ( node 1 + node 2 + carry ) m o d 10 (\textit{node}_1 + \textit{node}_2 + \textit{carry}) \bmod 10 (node1+node2+carry)mod10,进位值为 ⌊ node 1 . val + node 2 . val + carry 10 ⌋ \Big\lfloor \dfrac{\textit{node}_1.\textit{val} + \textit{node}_2.\textit{val} + \textit{carry}}{10} \Big\rfloor ⌊10node1.val+node2.val+carry⌋,将进位值更新到 carry \textit{carry} carry。
第二阶段,只有一个链表有结点。如果两个链表的长度不同,则其中的一个链表会先遍历完,然后只剩下一个链表还有结点。此时可以将已经遍历完的链表对应的结点值看成 0 0 0,继续遍历和计算和。
第三阶段,只有进位部分。当两个链表都遍历完时,如果 carry \textit{carry} carry 大于 0 0 0,则需要将进位值继续填入表示和的链表中,直到进位值变成 0 0 0。
具体实现方面,可以创建一个哑节点,在计算表示和的链表时,将每个结点在哑节点后面依次添加,最后返回哑节点的后一个结点作为表示和的链表。
下图为计算 42 + 965 42 + 965 42+965 的例子。对于每一位,表示和的链表中的结点值等于该位的两个结点值与 carry \textit{carry} carry 之和模 10 10 10 的余数,然后更新 carry \textit{carry} carry,计算更高位。
代码
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyHead = new ListNode(0);
ListNode temp = dummyHead;
ListNode node1 = l1, node2 = l2;
int carry = 0;
while (node1 != null && node2 != null) {
int val = node1.val + node2.val + carry;
temp.next = new ListNode(val % 10);
carry = val / 10;
temp = temp.next;
node1 = node1.next;
node2 = node2.next;
}
while (node1 != null) {
int val = node1.val + carry;
temp.next = new ListNode(val % 10);
carry = val / 10;
temp = temp.next;
node1 = node1.next;
}
while (node2 != null) {
int val = node2.val + carry;
temp.next = new ListNode(val % 10);
carry = val / 10;
temp = temp.next;
node2 = node2.next;
}
while (carry != 0) {
temp.next = new ListNode(carry % 10);
carry /= 10;
temp = temp.next;
}
return dummyHead.next;
}
}
复杂度分析
-
时间复杂度: O ( max ( m , n ) ) O(\max(m, n)) O(max(m,n)),其中 m m m 和 n n n 分别是两个链表的长度。需要遍历两个链表中的全部结点,对于每个结点的计算为 O ( 1 ) O(1) O(1) 的时间,由于是同时遍历,因此时间复杂度取决于较长的链表长度。
-
空间复杂度: O ( 1 ) O(1) O(1)。除了返回值以外,使用的空间复杂度是常数。