题目描述:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
思路
题目中说每个数字逆序存储,那么对于链表来说,头结点就是对应的个位,往后依次是十位、百位... 当进行加法操作时,就是从个位开始相加的,每一位相加的和大于十就进一位;所以,这题就是从两个链表的头结点开始对应相加,然后用一个变量(carry)存储每一次的进位值,初始为零,每位相加一次就更新一次这个进位的值。
在进行逐位相加时,是通过while循环进行遍历两个链表的每一个结点,执行对应相加的操作的。一开始我们也不知道哪个链表长一些,所以用while(l1 || l2)进行遍历,这样能保证按照最长的那个链表的位数进行计算。这时就得注意,当遍历长的链表多出来的那几位时,短的链表的对应位已经是null了,这时是没办法再进行相加操作的,所以短的那个链表的位就得用0去替代,然后才能对应位相加。
解决这题主要注意下面五点:
1. 链表的表示方式
首先,理解题目中的链表表示方式是关键。链表中的每个节点包含一个数字,代表一个数的单个位。这些数字是逆序存储的,即链表的头节点代表数字的个位。
例如,数字 243 会被表示为链表 3 -> 4 -> 2。当我们按照这种方式存储数字时,可以直接从左到右(即从链表头到尾)进行加法操作,而不需要预先反转链表。
2. 加法与进位处理
加法的基本逻辑是将两个链表对应位置的数字相加,并处理进位。我们从两个链表的头节点开始,逐对节点进行相加,如果加法结果大于或等于10,则需要进位。
- 加法操作:对当前两个节点的值进行加法操作,如果前一步有进位,也需要加上进位值。
- 进位处理:如果相加结果大于或等于10,则下一步的加法操作需要加上进位(进位值为相加结果除以10的商)。
- 存储和更新结果:将相加的结果模10后得到的值存储在新链表的当前节点中,并更新指针移动到下一位置。
3. 链表长度不一
两个链表可能长度不同。处理这种情况的一个简单方法是在较短的链表末尾视为补零,直到两个链表都完全遍历完毕。在代码实现中,我们可以通过检查链表是否为null来判断是否已到达链表的末尾。如果一个链表已经遍历完毕,我们可以假设它剩余的所有位置的值为0。
4. 最后的进位
在两个链表都遍历完毕后,可能还有一个未处理的进位(比如最后一次相加结果大于等于10)。如果存在这样的进位,我们需要在结果链表的末尾添加一个节点,节点的值为进位值。
5. 结果链表的创建
为了简化操作,我们通常会创建一个哨兵(dummy)节点作为结果链表的头部。这样,我们可以使用一个指针遍历链表并添加新的节点,而不需要单独处理结果链表的头部。最后,返回哨兵节点的下一个节点作为真正的结果头节点。
代码实现:
// 定义链表结点的结构
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val);
this.next = (next===undefined ? null : next);
}
function addTwoNumbers(l1, l2) {
// head是起始指针,一直指向链表的头节点,最后返回的时候利用head返回即可,不用再去额外找头结点了
let head = new ListNode(0);
// node指针用于往下移动,处理每个结点的相加操作
let node = head;
// carry是进位,初始为0
let carry = 0;
// 加法是从个位开始加;因为链表是逆序,所以只需要从前往后遍历,依次相加即可
// 这里l1和l2其实就是两个结点,一开始是头节点,结点中有next属性记录着下一个结点的信息
while (l1 || l2) {
// 如果两个链表长度不一致,哪个短,哪个的结点就会先变成null,谁的结点为null时就补0
let l1Val = l1 ? l1.val : 0;
let l2Val = l2 ? l2.val : 0;
//对应位相加,还需要加上前一位的进位值
let sum = l1Val + l2Val + carry;
// 和除10,大于10就取1,小于10取0
carry = Math.floor(sum / 10);
// 结果链表上新增一个结点存储当前位的和
node.next = new ListNode(sum % 10);
// node向下移动,处理下一位
node = node.next;
// 当两个链表都没有遍历完时,将结点继续向后移动一位,while继续执行
if (l1) l1 = l1.next;
if (l2) l2 = l2.next;
}
// 两个数的最高位相加后有可能还有一位进位,这时需要在最后补上一个结点存储最后的进位
if (carry > 0) {
node.next = new ListNode(carry);
}
// 因为head相当于哨兵结点,指向链表的头节点,所以需要返回head.next
return head.next;
}