大家好,我是挨打的阿木木,爱好算法的前端摸鱼老。最近会频繁给大家分享我刷算法题过程中的思路和心得。如果你也是想提高逼格的摸鱼老,欢迎关注我,一起学习。
题目
445. 两数相加 II
给你两个 非空 链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
示例1:
输入: l1 = [7,2,4,3], l2 = [5,6,4]
输出: [7,8,0,7]
示例2:
输入: l1 = [2,4,3], l2 = [5,6,4]
输出: [8,0,7]
示例3:
输入: l1 = [0], l2 = [0]
输出: [0]
提示:
- 链表的长度范围为
[1, 100]
0 <= node.val <= 9
- 输入数据保证链表代表的数字无前导 0
进阶: 如果输入链表不能修改该如何处理?换句话说,不能对列表中的节点进行翻转。
错误示范
思路
刚开始拿到题目我想偷懒,你不是链表嘛我给你转成数字就完事了。然后咔咔一通操作,最后原地翻车。
- 把
l1
、l2
所有节点的值相加,转成数字; - 遍历总数,输出一个新建的链表。
代码
/**
* 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}
*/
var addTwoNumbers = function(l1, l2) {
const total = String(getListNumber(l1) + getListNumber(l2));
const n = total.length;
let result = new ListNode(0);
let prev = result;
// 拿总数来生成节点
for (let i = 0; i < n; i++) {
prev.next = new ListNode(total[i]);
prev = prev.next;
}
return result.next;
};
// 拿到当前链表的数字值
function getListNumber(list) {
let str = "";
while (list) {
str += list.val;
list = list.next;
}
return Number(str);
}
翻车现场
好家伙,直接原地翻车,超出数字范围了直接,说明这道题目没这么简单,接下来正儿八经的来给大家做题。
常规解法
思路
- 首先我们进行数字的加减,都是从个位数开始加起,所以我们需要对两个链表做一个翻转;
- 然后从末尾节点加到头节点,同时需要用一个变量
add
来记录是否需要加1; - 每一轮两个链表同时往前进,直到有一个链表走完了,跳出循环;
- 然后需要就剩下单链表的情况了, 但是要进入到第二次循环,比如
9999 + 1
要一层层往前进1; - 进到最后的时候,如果已经遍历完链表了,还需要进1,那么需要新增一个节点;
- 最后对统计完的结果做一次翻转。
实现
/**
* 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}
*/
var addTwoNumbers = function(l1, l2) {
l1 = reverseList(l1);
l2 = reverseList(l2);
let result = new ListNode(0);
let prev = result;
let add = false;
// 第一轮, 两个数字都有值,直接相加判断是否进1
while (l1 && l2) {
// 判断上一轮是否需要往前进1位
let cur = l1.val + l2.val + (add ? 1 : 0);
if (cur >= 10) {
add = true;
cur -= 10;
} else {
add = false;
}
prev.next = new ListNode(cur);
l1 = l1.next;
l2 = l2.next;
prev = prev.next;
}
prev.next = l1 || l2;
// 如果只剩下一个值,或者一个值都没有了。还需要再进1
if (add) {
// 第二轮,用while是为了兼容9999这种情况需要层层递进
while (prev.next && add) {
prev = prev.next;
// 判断上一轮是否需要往前进1位
let cur = prev.val + 1;
if (cur >= 10) {
add = true;
cur -= 10;
} else {
add = false;
}
prev.val = cur;
}
// 需要新增头节点
if (add) {
prev.next = new ListNode(1);
}
}
return reverseList(result.next);
};
// 翻转链表
function reverseList(list) {
let prev = null;
while (list) {
const next = list.next;
list.next = prev;
prev = list;
list = next
}
return prev;
}
栈解法
因为题目中进阶说了如果链表不能进行翻转怎么办,那我们需要用栈来存储, 然后待会儿从两个栈最后取元素一个个加,实际上本质跟翻转链表是一样的。
栈代码
/**
* 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}
*/
var addTwoNumbers = function(l1, l2) {
// 存放入栈
let stack1 = pushToStack(l1),
stack2 = pushToStack(l2);
let add = false; // 是否需要进1
let result = null; // 存储结果
while (stack1.length || stack2.length) {
let cur = add ? 1 : 0;
if (stack1.length) {
cur += stack1.pop().val;
}
if (stack2.length) {
cur += stack2.pop().val;
}
if (cur >= 10) {
cur -= 10;
add = true;
} else {
add = false;
}
result = insertHeadNode(result, new ListNode(cur));
}
// 最后判断是否需要进1
if (add) {
result = insertHeadNode(result, new ListNode(1));
}
return result;
};
// 插入头节点
function insertHeadNode(list, newNode) {
newNode.next = list;
list = newNode;
return list;
}
// 链表转成栈
function pushToStack(list, stack = []) {
while (list) {
stack.push(list);
list = list.next;
}
return stack;
}
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。