1 题目
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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- 题目数据保证列表表示的数字不含前导零
2 代码实现
c++
cpp
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {}
* ListNode(int x) : val(x), next(nullptr) {}
* ListNode(int x, ListNode *next) : val(x), next(next) {}
* };
*/
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(0);
ListNode* cur = dummy;
int carry = 0 ;
while (l1 != nullptr || l2 != nullptr || carry != 0 ){
int val1 = l1 ? l1 -> val : 0 ;
int val2 = l2 ? l2 -> val : 0 ;
int sum = val1 + val2 + carry ;
int curVal = sum % 10 ;
carry = sum / 10 ;
cur -> next = new ListNode(curVal);
cur = cur -> next ;
if (l1) l1 = l1 -> next ;
if (l2) l2 = l2 -> next ;
}
ListNode* result = dummy -> next ;
delete dummy ;
return result ;
}
};
js
javascript
/**
* 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 dummy = new ListNode(0);
let cur = dummy ;
let carry = 0 ;
while(l1!= null || l2 != null || carry != 0 ){
const val1 = l1 ? l1.val : 0 ;
const val2 = l2 ? l2.val : 0 ;
const sum = val1 + val2 + carry ;
carry = Math.floor(sum / 10) ;
let curVal = sum % 10 ;
cur.next = new ListNode(curVal);
cur = cur.next ;
if(l1) l1 = l1.next ;
if(l2) l2 = l2.next ;
}
res = dummy.next ;
return res;
};
思考
我的玛雅,这什么啊,我一看感觉就要存三个变量,list1 中节点的val ,list2 中节点的val ,两个val相加的进位,需要取余操作吧,然后这个进位又要和下一个相加。
尝试用cpp写一遍,写好睡觉。。写不来,是要一直在算一个val吗?好奇怪!!!我做的是一直创建一个节点,录入这个节点的值的工作吗?
但是我不知道具体代码怎么写。。唉
题解(c++)
你对题目的核心思路判断完全正确!核心就是逐位取两个链表的节点值 + 进位值,通过取余得到当前位结果、整除得到新的进位,再依次创建新节点串联成结果链表,这就是本题的核心解法。下面一步步帮你把思路落地成可运行的 C++ 代码,同时讲清所有关键细节。
一、先定义链表节点结构
LeetCode 中该题的链表节点定义是固定的,先定义好基础结构:
cpp
// 链表节点定义(题目给定)
struct ListNode {
int val; // 存储当前位数字(0-9)
ListNode *next; // 指向下一个节点的指针
// 构造函数:方便创建节点(直接传值,next默认空)
ListNode() : val(0), next(nullptr) {}
ListNode(int x) : val(x), next(nullptr) {}
ListNode(int x, ListNode *next) : val(x), next(next) {}
};
二、核心解题代码(完整可运行)
采用迭代法实现(最直观、效率最高,时间复杂度 O (max (n,m)),n/m 是两个链表的长度),代码带详细注释,完全贴合你的思路:
cpp
#include <iostream>
using namespace std;
// 先粘贴上面的ListNode定义
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(0); // 哑节点(哨兵节点),简化头节点处理
ListNode* cur = dummy; // 遍历指针,用于创建新节点
int carry = 0; // 进位值,初始为0(无进位)
// 循环条件:l1没遍历完 OR l2没遍历完 OR 还有进位(三个条件满足一个就继续)
while (l1 != nullptr || l2 != nullptr || carry != 0) {
// 取l1当前节点值:不为空则取val,否则取0(链表遍历完的补0操作)
int val1 = l1 ? l1->val : 0;
// 取l2当前节点值:同理
int val2 = l2 ? l2->val : 0;
// 核心计算:当前位总和 = l1值 + l2值 + 上一位进位
int sum = val1 + val2 + carry;
// 当前位结果:总和对10取余(比如sum=10,当前位是0)
int curVal = sum % 10;
// 新的进位:总和除以10(整数除法,比如sum=10,进位是1)
carry = sum / 10;
// 创建新节点存储当前位结果,挂到cur后面
cur->next = new ListNode(curVal);
cur = cur->next; // cur后移,准备创建下一个节点
// l1、l2非空则后移,空则保持null(后续补0)
if (l1) l1 = l1->next;
if (l2) l2 = l2->next;
}
// 结果链表是哑节点的下一个节点(跳过初始的0节点)
ListNode* result = dummy->next;
delete dummy; // 释放哑节点内存,避免内存泄漏
return result;
}
};
// 测试代码:辅助函数(创建链表、打印链表)
ListNode* createList(int arr[], int n) {
if (n == 0) return nullptr;
ListNode* head = new ListNode(arr[0]);
ListNode* cur = head;
for (int i = 1; i < n; i++) {
cur->next = new ListNode(arr[i]);
cur = cur->next;
}
return head;
}
void printList(ListNode* head) {
ListNode* cur = head;
while (cur) {
cout << cur->val;
if (cur->next) cout << " -> ";
cur = cur->next;
}
cout << endl;
}
// 主函数测试
int main() {
// 示例1:l1=[2,4,3], l2=[5,6,4] → 输出[7,0,8]
int arr1[] = {2,4,3};
int arr2[] = {5,6,4};
ListNode* l1 = createList(arr1, 3);
ListNode* l2 = createList(arr2, 3);
Solution sol;
ListNode* res = sol.addTwoNumbers(l1, l2);
cout << "示例1结果:";
printList(res);
// 示例3:l1=[9,9,9,9,9,9,9], l2=[9,9,9,9] → 输出[8,9,9,9,0,0,0,1]
int arr3[] = {9,9,9,9,9,9,9};
int arr4[] = {9,9,9,9};
ListNode* l3 = createList(arr3, 7);
ListNode* l4 = createList(arr4, 4);
ListNode* res2 = sol.addTwoNumbers(l3, l4);
cout << "示例3结果:";
printList(res2);
return 0;
}
三、核心思路拆解(完全贴合你的想法)
你想到的三个核心变量(l1.val、l2.val、进位)、取余操作都是本题的关键,这里把思路理得更清晰:
- 哑节点(dummy):解决「结果链表头节点不好处理」的问题,不用单独判断头节点是否为空,直接在哑节点后挂新节点即可;
- 逐位计算 :循环遍历两个链表,每次取当前节点值(空则补 0)+ 进位,计算总和;
- 取余 + 整除 :
- 总和 % 10 → 得到当前位的数字(存入新节点);
- 总和 / 10 → 得到下一位的进位(整数除法,0 或 1,因为两个个位数 + 进位最大是 9+9+1=19);
- 循环终止条件:必须同时满足「l1 遍历完、l2 遍历完、进位为 0」,否则会漏掉最后一位的进位(比如示例 3,最后进位 1 需要单独创建节点);
- 节点创建:每次计算完当前位,就创建一个新节点,挂到结果链表的末尾,这就是你说的「一直创建节点、录入节点值」的工作,是本题的核心操作。
四、代码运行结果
示例1结果:7 -> 0 -> 8
示例3结果:8 -> 9 -> 9 -> 9 -> 0 -> 0 -> 0 -> 1
完全匹配题目示例要求。
五、关键细节提醒
- 链表补 0:当其中一个链表遍历完时,后续的节点值按 0 处理,避免空指针访问;
- 最后进位 :循环结束条件必须包含
carry != 0,否则像999 + 999 = 1998这样的用例,最后一位的进位 1 会丢失; - 内存释放:哑节点是临时创建的,最后要手动释放,避免 C++ 中的内存泄漏;
- 指针后移:l1、l2、cur 指针只有在非空时才后移,否则保持 null。
总结
- 核心解法:逐位求和(l1.val + l2.val + 进位)→ 取余存当前位 → 整除更新进位 → 循环创建新节点;
- 关键技巧:用哑节点简化结果链表的头节点处理,避免繁琐的空判断;
- 循环条件:
l1 != nullptr || l2 != nullptr || carry != 0(三个条件缺一不可); - 你的初始思路完全正确,只是需要把「存三个变量、取余、创建节点」的步骤用循环串联起来,这就是本题的完整解法。
自己写的错误百出的js代码
javascript
/**
* 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 dummy = new ListNode(0);
cur = dummy ;
let carry = 0 ;
while(l1!= null || l2 != null || carry != 0 ){
let val1 = l1 ? l1.val : 0 ;
let val2 = l2 ? l2.val : 0 ;
let sum = val1 + val2 + carry ;
carry = sum / 10 ;
let curVal = sum % 10 ;
cur.next = new ListNode(curVal);
cur = cur.next ;
if(l1) l1 = l1.next ;
if(l2) l2 = l2.next ;
}
res = dummy.next ;
return res;
};
你这份 JavaScript 代码的核心解题逻辑完全正确 ,仅存在2 个未修复的关键问题(也是之前提到的致命错误),导致代码无法正确运行,修复后即可在 LeetCode 直接提交通过。下面先明确剩余问题,再给出最终可运行代码,同时补充 JS 专属的优化细节~
一、当前代码仍存在的 2 个致命问题(必须修复)
问题 1:变量cur、res未用let/const声明,形成隐式全局变量
JavaScript 中未显式声明的变量会挂载到全局作用域,不仅容易和其他代码冲突,在严格模式下会直接抛出未定义变量错误,这是语法层面的硬性问题。
javascript
cur = dummy ; // 未声明,错误
// 正确:let cur = dummy;
res = dummy.next ; // 未声明,错误
// 正确:const res = dummy.next;
问题 2:进位计算用普通除法/,返回浮点数导致逻辑错误
两个个位数 + 进位的最大值为 9+9+1=19,进位只能是整数 0 或 1 。普通除法/会返回浮点数(比如sum=9时,9/10=0.9),浮点数参与下一次求和会导致计算偏差,必须用Math.floor()取整数商。
javascript
carry = sum / 10 ; // 浮点数进位,错误
// 正确:carry = Math.floor(sum / 10);
二、最终可直接提交的完整修正代码(带详细注释)
修复所有错误 + 优化代码规范,兼容题目所有测试用例(示例 1/2/3 均能正确输出):
javascript
/**
* 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) {
// 1. 创建哑节点(哨兵节点),简化头节点处理,必须加new
const dummy = new ListNode(0);
// 2. 显式声明遍历指针,块级作用域,避免全局变量
let cur = dummy;
// 3. 进位初始值为0,无进位
let carry = 0;
// 循环条件:l1未遍历完 || l2未遍历完 || 还有进位(三者满足其一就继续)
while (l1 !== null || l2 !== null || carry !== 0) {
// 取l1当前值:非空取val,空则补0(避免空指针访问)
const val1 = l1 ? l1.val : 0;
// 取l2当前值:同上
const val2 = l2 ? l2.val : 0;
// 核心计算:当前位总和 = l1值 + l2值 + 上一位进位
const sum = val1 + val2 + carry;
// 更新进位:取整数商,保证进位是0/1(关键修复)
carry = Math.floor(sum / 10);
// 当前位结果:总和对10取余,得到0-9的有效数字
const curVal = sum % 10;
// 创建新节点存储当前位结果,挂到cur后(必须加new)
cur.next = new ListNode(curVal);
// cur后移,准备创建下一个节点
cur = cur.next;
// l1非空则后移,空则保持null(后续补0)
if (l1) l1 = l1.next;
// l2非空则后移,空则保持null(后续补0)
if (l2) l2 = l2.next;
}
// 显式声明结果变量,指向哑节点下一个节点(有效结果头节点,关键修复)
const res = dummy.next;
// 返回结果链表
return res;
};
三、额外优化的代码规范点(提升可读性,非必须但推荐)
- 空判断从
!= null改为!== null:使用严格相等运算符,避免 JavaScript 隐式类型转换,代码逻辑更清晰; - 局部变量用
const声明:val1/val2/sum/curVal/res这些后续不修改的变量,用const替代let,符合 JS 编程规范,明确变量不可变; - 统一语句结尾分号:避免 JavaScript 自动分号插入(ASI)机制带来的语法歧义,这是通用的前端编程规范。
四、测试用例验证(和题目要求完全一致)
- 示例 1:
l1=[2,4,3]、l2=[5,6,4]→ 输出[7,0,8](342+465=807); - 示例 2:
l1=[0]、l2=[0]→ 输出[0](0+0=0); - 示例 3:
l1=[9,9,9,9,9,9,9]、l2=[9,9,9,9]→ 输出[8,9,9,9,0,0,0,1](9999999+9999=10009998)。
核心总结
你这份代码的解题思路毫无问题 ,剩余问题均集中在JavaScript 基础语法细节(变量声明、整数除法),这是从其他语言转写 JS 时的常见踩坑点,并非逻辑问题。
3 小结
思路很丝滑,每天多练多写,然后最后找个模块回头来小结一下。算法题的进度真的要加快了,加油。。!!