题目
标题和出处
标题:两数相加 II
出处:445. 两数相加 II
难度
4 级
题目描述
要求
给你两个非空的链表,表示两个非负的整数。数字最高位位于链表开始位置,并且每个结点只存储一位数字。将这两数相加会返回一个新的链表。将两个数相加,并返回一个表示和的链表。
你可以假设除了数字 0 \texttt{0} 0 之外,这两个数字都不会以 0 \texttt{0} 0 开头。
示例
示例 1:
输入:
l1
=
[7,2,4,3],
l2
=
[5,6,4]
\texttt{l1 = [7,2,4,3], l2 = [5,6,4]}
l1 = [7,2,4,3], l2 = [5,6,4]
输出:
[7,8,0,7]
\texttt{[7,8,0,7]}
[7,8,0,7]
示例 2:
输入:
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]
输出:
[8,0,7]
\texttt{[8,0,7]}
[8,0,7]
示例 3:
输入:
l1
=
[0],
l2
=
[0]
\texttt{l1 = [0], l2 = [0]}
l1 = [0], l2 = [0]
输出:
[0]
\texttt{[0]}
[0]
数据范围
- 每个链表中的结点数在范围 [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。
第二阶段,只有一个栈有结点。如果两个链表的长度不同,则两个栈的元素个数也不同,其中的一个栈会先变成空,然后只剩下一个栈还有结点。此时只需要考虑非空的栈,继续将结点弹出和计算和。
第三阶段,只有进位部分。当两个栈都变成空时,如果 carry \textit{carry} carry 大于 0 0 0,则需要将进位值继续填入表示和的链表中,直到进位值变成 0 0 0。
具体实现方面,使用一个结果栈存储结果,在计算结果链表时,将每个结点依次加入结果栈,则结果栈的栈底元素为结果链表的尾结点,即最低位,栈顶元素为结果链表的头结点,即最高位。用 nodeSum \textit{nodeSum} nodeSum 表示加入结果栈的结点,在将 nodeSum \textit{nodeSum} nodeSum 加入结果栈之前,如果结果栈不为空,则获得结果栈的栈顶结点,并将 nodeSum \textit{nodeSum} nodeSum 的 next \textit{next} next 指针指向栈顶结点,创建结果栈内的各结点的链接关系,从而得到结果链表。
计算结束之后,结果栈的栈顶结点即为结果链表的头结点。
下图为计算 42 + 965 42 + 965 42+965 的例子。存储输入结点的两个栈的栈顶在右边,栈底在左边,存储结果结点的栈的栈顶在左边,栈底在右边。数位相同的结点在图中对齐,结点之间的连接关系在图中省略。
代码
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Deque<ListNode> stack1 = new ArrayDeque<ListNode>();
Deque<ListNode> stack2 = new ArrayDeque<ListNode>();
ListNode temp1 = l1, temp2 = l2;
while (temp1 != null) {
stack1.push(temp1);
temp1 = temp1.next;
}
while (temp2 != null) {
stack2.push(temp2);
temp2 = temp2.next;
}
Deque<ListNode> stackSum = new ArrayDeque<ListNode>();
int carry = 0;
while (!stack1.isEmpty() && !stack2.isEmpty()) {
ListNode node1 = stack1.pop(), node2 = stack2.pop();
int val = node1.val + node2.val + carry;
ListNode nodeSum = new ListNode(val % 10);
if (!stackSum.isEmpty()) {
nodeSum.next = stackSum.peek();
}
stackSum.push(nodeSum);
carry = val / 10;
}
while (!stack1.isEmpty()) {
ListNode node1 = stack1.pop();
int val = node1.val + carry;
ListNode nodeSum = new ListNode(val % 10);
if (!stackSum.isEmpty()) {
nodeSum.next = stackSum.peek();
}
stackSum.push(nodeSum);
carry = val / 10;
}
while (!stack2.isEmpty()) {
ListNode node2 = stack2.pop();
int val = node2.val + carry;
ListNode nodeSum = new ListNode(val % 10);
if (!stackSum.isEmpty()) {
nodeSum.next = stackSum.peek();
}
stackSum.push(nodeSum);
carry = val / 10;
}
while (carry != 0) {
ListNode curNode = new ListNode(carry % 10);
if (!stackSum.isEmpty()) {
curNode.next = stackSum.peek();
}
stackSum.push(curNode);
carry /= 10;
}
return stackSum.peek();
}
}
复杂度分析
-
时间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m 和 n n n 分别是两个链表的长度。需要分别将两个链表中的全部结点入栈,时间复杂度是 O ( m + n ) O(m + n) O(m+n),计算两数相加的结果时需要同时反向遍历两个链表,时间复杂度是 O ( max ( m , n ) ) O(\max(m, n)) O(max(m,n)),因此总时间复杂度是 O ( m + n ) O(m + n) O(m+n)。
-
空间复杂度: O ( m + n ) O(m + n) O(m+n),其中 m m m 和 n n n 分别是两个链表的长度。空间复杂度主要取决于存储输入链表和结果链表的结点的栈空间。