两数相加
- 问题描述
- 问题分析
- 解题思路
- 代码实现
- 代码解析
- 注意事项
- 示例运行
- 总结
问题描述
给定两个非空链表,表示两个非负整数。链表中的每个节点存储一个数字,数字的存储顺序为逆序(即个位在链表头部)。要求将这两个数字相加,并以相同形式返回一个表示和的链表。
示例:
-
输入:
l1 = [2,4,3]
,l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807
-
输入:
l1 = [0]
,l2 = [0]
输出:[0]
-
输入:
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
,其next
指针指向结果链表的头节点。 - 遍历链表:同时遍历两个链表,逐位相加,并处理进位。
- 处理剩余部分:如果其中一个链表先遍历完,继续处理另一个链表的剩余部分。
- 处理最终进位:如果最后还有进位,需要在结果链表末尾添加一个新节点。
代码实现
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {// 创建虚拟头节点struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));struct ListNode* current = dummy;int carry = 0; // 进位标志// 遍历两个链表while (l1 || l2) {// 获取当前节点的值(如果链表为空,则为0)int n1 = l1 ? l1->val : 0;int n2 = l2 ? l2->val : 0;// 计算当前位的和int sum = n1 + n2 + carry;carry = sum / 10; // 更新进位sum = sum % 10; // 当前位的值// 创建新节点存储当前位的值current->next = (struct ListNode*)malloc(sizeof(struct ListNode));current->next->val = sum;current->next->next = NULL;current = current->next;// 移动链表指针if (l1) {l1 = l1->next;}if (l2) {l2 = l2->next;}}// 如果最后还有进位,添加一个新节点if (carry > 0) {current->next = (struct ListNode*)malloc(sizeof(struct ListNode));current->next->val = carry;current->next->next = NULL;}// 返回结果链表的头节点return dummy->next;
}
代码解析
- 虚拟头节点:
dummy
是一个虚拟头节点,用于简化链表操作。最终返回的结果链表是dummy->next
。 - 逐位相加:通过
n1
和n2
获取当前节点的值,加上进位carry
,计算当前位的和sum
。 - 进位处理:
carry = sum / 10
用于更新进位,sum = sum % 10
用于获取当前位的值。 - 链表遍历:通过
l1
和l2
的指针移动,逐位处理两个链表。 - 最终进位:如果最后还有进位,需要在结果链表末尾添加一个新节点。
注意事项
- 链表为空的处理:在计算
n1
和n2
时,需要判断链表是否为空,避免访问空指针。 - 内存管理:每次创建新节点时,需要使用
malloc
分配内存,并确保在程序结束时释放内存(如果需要)。
示例运行
以示例 1 为例:
- 输入:
l1 = [2,4,3]
,l2 = [5,6,4]
- 计算过程:
- 第一位:
2 + 5 = 7
,无进位,结果链表为[7]
- 第二位:
4 + 6 = 10
,进位为 1,当前位为 0,结果链表为[7,0]
- 第三位:
3 + 4 + 1 = 8
,无进位,结果链表为[7,0,8]
- 第一位:
- 输出:
[7,0,8]
总结
本题考察了链表的操作和进位处理。通过创建虚拟头节点和逐位相加的方式,可以高效地解决该问题。时间复杂度为 O(max(n, m)),其中 n 和 m 分别为两个链表的长度。
最开始我还以为是正常的相加呢,那不得麻烦死,还得反转列表。结果不需要,直接加就好,一下子容易太多了。另外学会了如何新建一个结点, struct ListNode* dummy = (struct ListNode*)malloc(sizeof(struct ListNode));我就一直想为什么(ListNode*)malloc(sizeof(struct ListNode));为什么不对,原来是设置的就有问题。在代码中,dummy 和 current 的类型声明为 struct ListNode*,但在 malloc 和类型转换时,使用了 (ListNode*),这可能会导致编译错误。正确的类型转换应该是 (struct ListNode*)。
在C语言中,malloc 返回的是 void* 类型。虽然在某些编译器中,显式类型转换是允许的,但严格来说,这种转换是不必要的。如果你的编译器支持,可以去掉类型转换。