题目难度: 中等
今天继续更新程序员面试金典系列, 大家在公众号 算法精选 里回复 面试金典 就能看到该系列当前连载的所有文章了, 记得关注哦~
题目描述
给定两个用链表表示的整数,每个节点包含一个数位。
这些数位是反向存放的,也就是个位排在链表首部。
编写函数对这两个整数求和,并用链表形式返回结果。
示例:
- 输入:(7 -> 1 -> 6) + (5 -> 9 -> 2),即 617 + 295
- 输出:2 -> 1 -> 9,即 912
题目思考
- 假设这些数位是正向存放的,又该如何解决呢?
示例:
- 输入:(6 -> 1 -> 7) + (2 -> 9 -> 5),即 617 + 295
- 输出:9 -> 1 -> 2,即 912
解决方案
思路
- 因为数位是反向存放的, 所以可以直接利用逐位相加: 将每一位进行求和, 如果大于 10 的话进位为 1, 放在下一位继续求和即可
- 特别注意最高位进位为 1 的特殊情况, 比如 99+1. 所以可以将进位是否为 0 也作为循环的条件, 统一起来, 从而避免遗漏, 具体参见下面的代码部分
- 接下来考虑进阶问题: 节点正向存放时怎么解决?
- 很容易想到的思路就是将输入链表进行翻转, 求完和之后再翻转一次, 从而使得最终的和也满足正向存放. 对应下面代码中的 addTwoNumbersInForwardDirection 方法
复杂度
- 时间复杂度 O(M+N): 只需要遍历每个节点一遍
- 空间复杂度 O(max(M,N)): 需要新建一个链表, 其节点数目为 M 或 N 的较大值(也可能多 1, 对应的是最高位进位为 1 的情况). 如果允许修改输入节点的话, 则可以直接在某个链表基础上改值, 从而将空间复杂度降为 O(1)
代码
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
# 使用哨兵节点, 作为新的链表头的前一位
dummy = ListNode(0)
cur = dummy
# carry存储当前进位
carry = 0
# 将carry是否为0也作为循环条件, 如果两个链表都遍历完毕但仍有进位时, 也需要进行处理
while l1 or l2 or carry:
if l1:
carry += l1.val
l1 = l1.next
if l2:
carry += l2.val
l2 = l2.next
# 求当前位的和, 以及新的进位
carry, sm = divmod(carry, 10)
cur.next = ListNode(sm)
cur = cur.next
return dummy.next
def addTwoNumbersInForwardDirection(self, l1: ListNode,
l2: ListNode) -> ListNode:
# 正向存放的话
# 先转成反向, 然后调用addTwoNumbers, 最后再把结果反过来
def reverse(root):
if not root:
return root
# 经典三指针链表翻转
pre = None
cur = root
while cur:
nex = cur.next
cur.next = pre
pre = cur
cur = nex
return pre
return reverse(self.addTwoNumbers(reverse(l1), reverse(l2)))
大家可以在下面这些地方找到我~😊
我的公众号: 算法精选, 欢迎大家扫码关注~😊