2. 两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例 1:
输入:l1 = [2,4,3], l2 = [5,6,4] 输出:[7,0,8] 解释:342 + 465 = 807.
示例 2:
输入:l1 = [0], l2 = [0] 输出:[0]
示例 3:
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9] 输出:[8,9,9,9,0,0,0,1]
提示:
每个链表中的节点数在范围 [1, 100] 内 0 <= Node.val <= 9 题目数据保证列表表示的数字不含前导零
题目分析
两个数字以逆序方式存储在链表中,返回两个数相加的结果链表(也是逆序存储的)
解法分析
这里是逆序存储的,遍历链表就是从个位开始,符合加法的习惯顺序
题目主要考察链表双指针技巧和加法运算过程中对进位的处理
链表算法题常见【虚拟头结点】技巧,也就是dummy节点,可以避免处理初始的空指针情况,降低代码的复杂性
使用虚拟头节点
/**
* Definition for singly-linked list.
*/
function ListNode(val, next) {
this.val = (val === undefined ? 0 : val)
this.next = (next === undefined ? null : next)
}
// 使用头节点
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
let addTwoNumbers = function(l1, l2) {
let list1 = l1;
let list2 = l2;
// 虚拟头节点
let templist = new ListNode(-1);
// 第一次挂载
let result = templist;
let carry = 0; // 进位
// 开始执行加法,两条链表走完且没有进位时才能结束循环
while (list1 || list2 || carry) {
let sum = carry;
if (list1) {
sum += list1.val;
list1 = list1.next;
}
if (list2) {
sum += list2.val;
list2 = list2.next;
}
carry = Math.floor(sum / 10); // 进位值
sum = sum % 10;
// 构建新节点
templist.next = new ListNode(sum % 10);
// console.log('templist',templist);
// console.log('result',result);
// templist指向改变,result指向不变,所以最后返回的是result.next 去掉虚拟头节点的值
templist = templist.next;
}
// 返回结果链表的头结点(去除虚拟头结点) result是templist
return result.next;
};
let l1_1 = new ListNode(2);
let l1_2 = new ListNode(4);
let l1_3 = new ListNode(3);
l1_1.next = l1_2;
l1_2.next = l1_3;
let l2_1 = new ListNode(5);
let l2_2 = new ListNode(6);
let l2_3 = new ListNode(4);
l2_1.next = l2_2;
l2_2.next = l2_3;
const input1 = l1_1;
const input2 = l2_1;
let result = addTwoNumbers(input1, input2);
console.log(result);
不使用虚拟头节点
/**
* Definition for singly-linked list.
*/
function ListNode(val, next) {
this.val = (val === undefined ? 0 : val)
this.next = (next === undefined ? null : next)
}
// 不使用头节点
/**
* @param {ListNode} l1
* @param {ListNode} l2
* @return {ListNode}
*/
let addTwoNumbers = function(l1, l2) {
let list1 = l1;
let list2 = l2;
let templist ;
let result ;
let carry = 0; // 进位
// 开始执行加法,两条链表走完且没有进位时才能结束循环
while (list1 || list2 || carry) {
let sum = carry;
if (list1) {
sum += list1.val;
list1 = list1.next;
}
if (list2) {
sum += list2.val;
list2 = list2.next;
}
carry = Math.floor(sum / 10); // 进位值
sum = sum % 10;
if (!templist) {
// 第一次创建节点
result = new ListNode(sum);
templist = result;
} else {
// 创建新节点
templist.next = new ListNode(sum);
// templist指向改变,result指向不变,所以最后返回的是result
templist = templist.next;
}
}
return result;
};
let l1_1 = new ListNode(2);
let l1_2 = new ListNode(4);
let l1_3 = new ListNode(3);
l1_1.next = l1_2;
l1_2.next = l1_3;
let l2_1 = new ListNode(5);
let l2_2 = new ListNode(6);
let l2_3 = new ListNode(4);
l2_1.next = l2_2;
l2_2.next = l2_3;
const input1 = l1_1;
const input2 = l2_1;
let result = addTwoNumbers(input1, input2);
console.log(result);
总结小技巧
链表中使用虚拟头节点技巧,定义一个临时变量,存储下一个节点,最后结果在原来的位置不动
如果是使用了虚拟头节点,最后节返回的是result.next,如果不使用虚拟头节点,最后返回的是result