目录
题目
思路
- 同时遍历两个链表,对应位置的数字相加
- 处理可能产生的进位
- 将每一位的计算结果存入新链表
算法步骤
创建虚拟头节点,简化链表操作
维护一个进位变量carry,初始为0
同时遍历两个链表,只要有一个链表还有节点或者有进位,就继续计算
对于每一位的计算:
- 取两个链表当前位置的值(如果链表已结束则为0)
- 加上上一位的进位
- 计算当前位的结果(和对10取模)和新的进位(和整除10)
- 将当前位的结果添加到结果链表
返回结果链表(跳过虚拟头节点)
4. 关键点
- 逆序存储的优势:正好符合从低位到高位的计算顺序
- 虚拟头节点:简化链表操作,不需要特殊处理头节点
- 统一循环条件:while(l1 || l2 || carry)确保处理所有情况
- 安全访问:检查指针是否为空,避免空指针异常
- 进位处理:每一位计算完后,将进位传递到下一位
时间和空间复杂度
- 时间复杂度:O(max(m,n)),其中m和n是两个链表的长度
- 空间复杂度:O(max(m,n)),用于存储结果链表
过程
l1 = [2,4,3] // 表示数字342
l2 = [5,6,4] // 表示数字465
初始状态
虚拟头节点: [0] -> null
prev指向虚拟头节点
t = 0
cur1指向l1的头节点: [2] -> [4] -> [3] -> null
cur2指向l2的头节点: [5] -> [6] -> [4] -> null
第一次循环
- 读取cur1的值: t += 2, t = 2
- cur1前进: cur1 -> [4] -> [3] -> null
- 读取cur2的值: t += 5, t = 7
- cur2前进: cur2 -> [6] -> [4] -> null
- 创建新节点: [7]
- 添加到结果链表: [0] -> [7] -> null
- prev前进: prev -> [7]
- 计算进位: t = 7 / 10 = 0
结果链表: [0] -> [7] -> null
↑
prev
t = 0
cur1 -> [4] -> [3] -> null
cur2 -> [6] -> [4] -> null
第二次循环
- 读取cur1的值: t += 4, t = 4
- cur1前进: cur1 -> [3] -> null
- 读取cur2的值: t += 6, t = 10
- cur2前进: cur2 -> [4] -> null
- 创建新节点: [0] (10 % 10 = 0)
- 添加到结果链表: [0] -> [7] -> [0] -> null
- prev前进: prev -> [0]
- 计算进位: t = 10 / 10 = 1
结果链表: [0] -> [7] -> [0] -> null
↑
prev
t = 1 (有进位)
cur1 -> [3] -> null
cur2 -> [4] -> null
第三次循环
- 读取cur1的值: t += 3, t = 4
- cur1前进: cur1 -> null
- 读取cur2的值: t += 4, t = 8
- cur2前进: cur2 -> null
- 创建新节点: [8]
- 添加到结果链表: [0] -> [7] -> [0] -> [8] -> null
- prev前进: prev -> [8]
- 计算进位: t = 8 / 10 = 0
结果链表: [0] -> [7] -> [0] -> [8] -> null
↑
prev
t = 0
cur1 -> null
cur2 -> null
循环结束
- cur1 = null, cur2 = null, t = 0,不满足继续循环的条件
- 跳过虚拟头节点,返回结果链表: [7] -> [0] -> [8] -> null
最终结果
[7,0,8] // 表示数字807
读者可能出现的错误写法
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(0);
ListNode* newhead = dummy;
ListNode* cur1 = l1;
ListNode* cur2 = l2;
int carry = 0; // 定义进位变量
while(cur1 && cur2)
{
int nums = 0;
nums += cur1->val;
cur1 = cur1->next;
nums += cur2->val;
cur2 = cur2->next;
newhead->next = new ListNode(nums%10);
if(nums % 10 >1)
{
carry += 1;
}
}
}
};
循环条件错误:while(cur1 && cur2)只处理两个链表长度相同的部分,忽略了较长链表的剩余部分和可能的进位
// 原代码
while(cur1 && cur2)
// 修改为
while(cur1 || cur2 || carry)
原因: 原循环条件只在两个链表都有节点时执行,会忽略较长链表的剩余部分和最后可能存在的进位。修改后的条件确保处理所有情况:任一链表还有节点或存在进位时都会继续计算。
进位判断错误:if(nums % 10 > 1)不是正确的进位计算方式,应该是carry = nums / 10
没有处理进位传递:计算出的进位没有加到下一位的计算中
// 原代码
int nums = 0;
// 修改为
int nums = carry;
没有移动结果链表指针:每次添加新节点后,newhead没有向前移动
// 原代码
newhead->next = new ListNode(nums%10);
// 修改为
newhead->next = new ListNode(nums%10);
newhead = newhead->next;
原因: 原代码没有移动结果链表的指针,导致每次新节点都会覆盖前一个节点,最终结果链表只有一个节点。修改后确保每次添加新节点后,指针移动到最新节点,保持链表的正确结构。
没有处理链表长度不同的情况:当一个链表比另一个长时,需要继续处理较长链表
// 原代码
nums += cur1->val;
cur1 = cur1->next;
nums += cur2->val;
cur2 = cur2->next;
// 修改为
if(cur1) {
nums += cur1->val;
cur1 = cur1->next;
}
if(cur2) {
nums += cur2->val;
cur2 = cur2->next;
}
原因: 原代码在链表长度不同时会导致空指针访问。修改后添加了空指针检查,只有当指针不为空时才访问其值,避免程序崩溃。
没有返回值:函数末尾缺少返回语句
正确的写法
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
ListNode* dummy = new ListNode(0);
ListNode* newnode = dummy;
ListNode* cur1 = l1;
ListNode* cur2 = l2;
int carry = 0;
while(cur1 || cur2 || carry)
{
int nums = carry;
if(cur1)
{
nums += cur1->val;
cur1 = cur1->next;
}
if(cur2)
{
nums += cur2->val;
cur2 = cur2->next;
}
newnode->next = new ListNode(nums%10);
newnode = newnode->next;
carry = nums/10;
}
ListNode* newhead = dummy->next;
delete dummy;
return newhead;
}
};