LeetCode LCR 025. 两数相加 II
题目描述


题意分析
两条非空链表分别表示非负整数,每个节点是一位十进制数,最高位在头部。求两数之和,并返回同样高位在前的新链表。
数字可能很长,不能先装进固定宽度整数再相加。两条链长度可以不同,计算要从个位向高位传递进位。下面用双栈完成计算,同时满足不能翻转输入链表的进阶要求。
解法:双栈从低位相加与头插
核心思路
[!blue]
加法依赖低位传来的进位,但单链表只能从高位往低位访问。分别将两个链表的数位按顺序压栈,最后进入的个位就会最先弹出,栈把读取方向转换成了计算方向。
每轮开始时,
carry表示此前低位产生的进位,只可能为零或一。分别弹出两个当前数位,某个栈已经空时补零,再累加进carry。累加后的值暂时表示本位总和,范围为零到十九;对十取余得到本位数字,整除十后才重新成为传给下一轮的进位。新数位的生成顺序是从低到高,结果却要求从高到低,因此将每个新节点插到当前结果头部。后算出的高位自然排在前面,不需要最终再反转。哑节点
dummy只提供固定入口,真正结果从dummy.next开始。两个栈都空后也不能立刻结束,还要检查是否留有最高位进位。循环同时覆盖“任一栈非空或进位非零”,就能自动创建需要的额外最高位。
整个过程只读取输入节点值并移动局部游标,不改变任何原节点连接;结果使用新节点构造。
解题步骤
- 分别遍历两条输入链表,将数位压入两个栈。
- 初始化进位为零,创建结果哑节点。
- 两栈或进位仍有内容时,弹出当前位并累加,空栈按零处理。
- 用总和余十创建新节点,接到已有结果头部。
- 将总和整除十作为新进位,继续处理;最后返回
dummy.next。
代码实现
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Deque<Integer> s1 = new ArrayDeque<>();
Deque<Integer> s2 = new ArrayDeque<>();
// 只读不改,压栈后弹出顺序即为从个位到最高位。
for (; l1 != null; l1 = l1.next) {
s1.push(l1.val);
}
for (; l2 != null; l2 = l2.next) {
s2.push(l2.val);
}
int carry = 0;
ListNode dummy = new ListNode();
while (!s1.isEmpty() || !s2.isEmpty() || carry != 0) {
carry += (s1.isEmpty() ? 0 : s1.pop()) + (s2.isEmpty() ? 0 : s2.pop());
// 头插法:后算出的高位自动排到前面,省掉最后一次反转。
ListNode node = new ListNode(carry % 10, dummy.next);
dummy.next = node;
carry /= 10;
}
return dummy.next;
}
}
func addTwoNumbers(l1 *ListNode, l2 *ListNode) *ListNode {
stack1 := []int{}
stack2 := []int{}
// 只读不改,出栈顺序即为从个位到最高位。
for l1 != nil {
stack1 = append(stack1, l1.Val)
l1 = l1.Next
}
for l2 != nil {
stack2 = append(stack2, l2.Val)
l2 = l2.Next
}
carry := 0
dummy := &ListNode{}
for len(stack1) > 0 || len(stack2) > 0 || carry > 0 {
if len(stack1) > 0 {
carry += stack1[len(stack1)-1]
stack1 = stack1[:len(stack1)-1]
}
if len(stack2) > 0 {
carry += stack2[len(stack2)-1]
stack2 = stack2[:len(stack2)-1]
}
// 头插法:后算出的高位自动排到前面。
node := &ListNode{Val: carry % 10, Next: dummy.Next}
dummy.Next = node
carry /= 10
}
return dummy.Next
}
复杂度分析
- 时间复杂度:$O(m+n)$,两条链各压栈一次,加法至多处理较长链的位数再加一。
- 空间复杂度:$O(m+n)$,两个栈保存全部数位;输出的新链表另外需要 $O(\max(m,n)+1)$。
关键点总结
[!green]
- 栈提供逆序访问,解决输入方向与进位方向不一致的问题。
carry在轮开始和结束时是进位,轮中累加后临时表示当前位总和。- 头插让低位先生成、高位后生成仍能得到高位在前的结果。
- 循环必须覆盖最终进位,短链按高位补零处理。
易错点总结
[!yellow]
- 从链头直接相加会在低位进位尚未知时就决定高位,计算方向错误。
- 只在两个栈都非空时循环,会丢掉长链剩余数位。
- 忽略最后进位,会少生成一个最高位。
- 头插前新节点要先保存旧结果头,再更新入口;使用保存好的独立指针时则不必机械要求赋值顺序。
- 不能将任意长度链表转成固定宽度整数,逐位计算才不会丢失高位。
相似题目
| 题目 | 难度 | 关联与区别 |
|---|---|---|
| 2. 两数相加 | 中等 | 原题低位在前,可顺序处理进位;本题高位在前,用栈或反转才能从低位开始。 |
| 415. 字符串相加 | 简单 | 同样模拟逐位加法,字符串可直接从末尾向前索引,链表需要补足逆向访问能力。 |
转载与许可
许可
本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处!