逆序存储很友好了,直接遍历链表就是从个位开始的,符合我们计算加法的习惯顺序。如果是正序存储,那倒要费点脑筋了,可能需要 翻转链表 或者使用栈来辅助。
这道题主要考察 链表双指针技巧 和加法运算过程中对进位的处理。注意这个 carry 变量的处理,在我们手动模拟加法过程的时候会经常用到。
代码中还用到一个链表的算法题中是很常见的「虚拟头结点」技巧,也就是 dummy 节点。你可以试试,如果不使用 dummy 虚拟节点,代码会稍显复杂,而有了 dummy 节点这个占位符,可以避免处理初始的空指针情况,降低代码的复杂性。
class Solution:
def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
p1, p2 = l1, l2
# 虚拟头结点(构建新链表时的常用技巧)
dummy = ListNode(-1)
p = dummy
# 记录进位
carry = 0
# 两条链表走完且没有进位时才能结束循环
# 注意即使p1和p2空指针的情况下carry也可能进位!
while p1 or p2 or carry:
# 注意p1和p2都有可能空指针
cur1 = p1.val if p1 else 0
cur2 = p2.val if p2 else 0
res = cur1 + cur2 + carry
# 注意先计算carry再计算res
carry = res // 10
res %= 10
# 构建新节点
p.next = ListNode(res)
# 注意p1和p2到空指针的情况下就不要再前进了
p1, p2, p = p1.next if p1 else None, p2.next if p2 else None, p.next
# 返回结果链表的头结点(去除虚拟头结点)
return dummy.next