题目描述
给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字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。
- 题目数据保证列表表示的数字不含前导零。
解题思路:
方法一:迭代
首先请看如何遍历一个链表,代码框架如下:
while(L1){//从链表头节点开始向后遍历,直到遇到空节点 printf("%d\n",L1->val);//输出当前节点 L1=L1->next;//准备遍历下一个节点 }
迭代的思路:初始化答案为一个[NULL链表],每次循环向该链表末尾添加一个节点(保存一个数位)。
循环即遍历链表L1和L2,每次把两个节点值L1.val和L2.val与进位值carry相加,除以10的余数即为当前节点需要保存的数位,除以10的商即为新的进位值。
注意:在首次循环时,无法在一个空节点的末尾添加节点,故创建一个哨兵节点(dummy node),当成初始的[NULL链表]。循环结束后,哨兵节点的下一个节点就是最终要返回的链表头节点。
struct ListNode* createListNode(int val) { struct ListNode* node=(struct ListNode*)malloc(sizeof(struct ListNode)); node->val = val; node->next = NULL; return node; } struct ListNode* addTwoNumbers(struct ListNode* L1,struct ListNode* L2) { //构造哨兵节点dummy,最后返回dummy.next,以方便处理新链表的头节点 struct ListNode* dummy = createListNode(0); struct ListNode* node = dummy; //node一直会变化(前进) int carry = 0;//进位 //只要没走到头的链表或进位不为0就一直前进 while(L1!=NULL || L2!=NULL || carry!=0) { //求和,考虑可能有链表走到头 int sum=(L1!=NULL ? L1->val:0)+(L2!=NULL ? L2->val:0)+carry; //在尾部添加节点 node->next=createListNode(sum%10); node = node->next; //更新进位,并向两个链表尾部前进 carry = sum/10; if(L1!=NULL) L1=L1->next; if(L2!=NULL) L2=L2->next; } struct ListNode* result dummy->next;//保存结果链表的头节点 free(dummy);//释放哨兵节点的内存 return result; }
复杂度分析:
- 时间复杂度:O(n),其中n为L1长度和L2长度的最大值。
- 空间复杂度:O(1),返回值不计入。
方法二:模拟
由于输入的两个链表都是逆序存储数字的位数,因此两个链表中同一位置的数字可以直接相加。如果两个链表的长度不同,则可以认为长度短的链表的后面有若干个0。
struct ListNode* addTwoNumbers(struct ListNode* L1,struct ListNode* L2) { struct ListNode* head = NULL,*tail = NULL; //定义头、尾指针都为NULL int carry=0; //初始化进位 while(L1||L2) { //定义n1、n2和求和位 int n1=L1?L1->val:0; int n2=L2?L2->val:0; int sum=n1+n2+carry; //从尾到头,一直遍历到头部截至 if(!head) { head = tail = malloc(sizeof(struct ListNode)); tail->val = sum%10; tail->next = NULL; }else{ tail->next = malloc(sizeof(struct ListNode)); tail->next->val = sum%10; tail = tail->next; tail->next = NULL; } carry =sum/10; if(L1) L1=L1->next; if(L2) L2=L2->next; } //两个数字相加,最后结果有可能会比原来数字的位数多1 if(carry>0) { tail->next = malloc(sizeof(struct ListNode)); tail->next->val=carry; tail->next->next=NULL; } }
复杂度分析:
- 时间复杂度:O(max(m,n)),其中m和n分别为两个了链表的长度。
- 空间复杂度:O(1)。
方法三:网上简易做法(2. 两数相加 - 力扣(LeetCode))
struct ListNode* addTwoNumbers(struct ListNode* L1,struct ListNode* L2) { //创造一个头结点,让头指针dummy指向它 struct ListNode* dummy = malloc(sizeof(struct ListNode)); //创造一个指针,让cur指向最后 struct ListNode* cur = dummy; //创造一个进位判断值 int t=0; //循环(L1指向非空或L2指向非空或进位值非空)就执行 while(L1 || L2 || t) { //如果L1指向非空,则将L1的值加到t上,L1指向L1的下一个结点。 if(L1) t=t+L1->val,L1=L1->next; if(L2) t=t+L2->val,L2=L2->next; //创造一个结点并赋值。本来没想给新结点的next赋值,可是报错啊, //如果不赋值就是野指针 cur->next = malloc(sizeof(struct ListNode)); cur->next->val = t%10; cur->next->next = NULL; //cur指向下一个,也就是cur指向了新结点 cur = cur->next; t=t/10; } return dummy->next; }