算法:两数相加
题目
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
1 | 输入:l1 = [2,4,3], l2 = [5,6,4] |
示例 2:
1 | 输入:l1 = [0], l2 = [0] |
示例 3:
1 | 输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] |
提示:
- 每个链表中的节点数在范围
[1, 100]
内 0 <= Node.val <= 9
- 题目数据保证列表表示的数字不含前导零
解法
1 | func addTwoNumbers(_ l1: ListNode?, _ l2: ListNode?) -> ListNode? { |
解题思路
两数相加问题的主要思路是,将两个数字表示成逆序链表,每个节点存储一个数字位。步骤如下:
从头节点开始相加:由于链表是逆序存储,链表的头节点就是数字的个位,从最低位开始相加。
计算和与进位:将对应的两个节点的值相加,同时加上上一次运算的进位,得到当前的和。
令当前位的值为
sum
模 10,即digit = sum % 10
更新进位为
sum
除以 10,即carry = sum / 10
构造结果链表:每计算出一位的值,就构造一个新的节点,将其接入结果链表中。
遍历所有节点:同时遍历两个链表,直到两个链表的所有节点都被处理完毕。如果还有进位,则需要在链表末尾插入一个新节点来表示该进位。
考虑特殊情况:如果输入链表为空或只存在其中一个链表的情况,也要正确处理。
代码实现详细讲解
下面详细解释代码中每一部分的功能和实现细节:
1 | if l1 == nil || l2 == nil { |
输入判断:
如果任意一个链表为空,直接返回非空的那个链表。
其中,l1 ?? l2
表示如果 l1
非空则返回 l1
,否则返回 l2
。
1 | var cur1 = l1 |
变量初始化:
cur1
和 cur2
分别作为遍历 l1
和 l2
的指针。首先对两个链表的头节点(个位)进行相加,计算总和
sum = cur1!.val + cur2!.val
。根据
sum
计算进位 carry = sum / 10
。新建结果链表的头节点,节点的值为
sum % 10
(当前位的结果)。定义一个
cur
指针指向当前处理的结果链表尾部,方便后续的节点追加。
1 | while cur1?.next != nil || cur2?.next != nil { |
遍历剩余节点:
使用 while
循环处理两个链表中剩余的节点,条件是任意一个链表还有后续节点。
通过
cur1?.next?.val ?? 0
获取 cur1
的下一个节点的值,如果不存在则赋值为 0。同理,
value2 = cur2?.next?.val ?? 0
获取 cur2
的下一个节点的值。将当前位上的两个数字及前一次的进位相加,得到新的
sum
。更新进位
carry = sum / 10
。新建一个节点保存当前位的值
sum % 10
并接到结果链表后面。移动指针
cur1
和 cur2
指向下一个节点,继续下一轮相加;同时更新 cur
指针指向新加入的节点。
1 | if carry > 0 { |
处理最后的进位:
循环结束后,可能最后一次运算后仍有进位(例如相加得到的和大于等于 10)。
如果 carry
大于 0,则新建一个节点保存进位,并将其接入链表末尾。