作者只是一个小白,最近希望能提升自己的代码水平,所以开始刷leetcode。写博客是为了整理自己的学习内容,难免会出错。如果有大大发现,非常欢迎指正哦!
题目:2、两数相加
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 0 之外,这两个数都不会以 0 开头。
实例:
输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
题解
这道题思路并不复杂,就是将l1,l2对应的数字和进位相加,相加的结果的个位数填入对应位置,相加的结果的十位数作为进位存储。最容易忘的一点就是最高位相加后可能也会有进位。
//伪码
//add为进位
while(l1 && l2 && add){
if(l1){
add = add + l1->val;
l1 = l1->next;
}
if(l2){
add = add + l2->val;
l2 = l2->next;
}
result->val = add % 10;//取个位数
add = add / 10;//取十位数
使result指向它的下一级;//形成链表
}
return result;
所以这应该主要看我们对单链表的操作。个人感觉这篇博客写得最全:C语言实现单链表的创建及基本操作
while循环
这边先贴我第一次写的错误 的代码
struct ListNode *addTwoNumbers(struct ListNode *l1, struct ListNode *l2)
{
int add = 0, val = 0;
struct ListNode *result;
result = malloc(sizeof(struct ListNode));
while (l1 || l2 || add)
{
if (l1)
{
val += l1->val;
l1 = l1->next;
}
if (l2)
{
val += l2->val;
l2 = l2->next;
}
val += add;
result->val = val % 10;
add = val / 10;
val = 0;
result->next = result;//问题主要在这
}
result = result->next;
return result;
}
这段代码里我主要有两个错误。一个是result->next = result。例如我要计算,12+23,根据题目的意思,我先算得result->val=2+3=5,然后将5存入到result->next->val中。按照我的想法是接下来计算十位数result->val=1+2=3,而这不会改变result->next->val中的值,然后就一层一层叠下去。但实际上首先我的想法是用头插法创建单链表,但题目要求的结果应该是用尾插法,其次最重要的一点是result是一个指针,里面存储的是地址,result->next = result这一句实际上是让result里每一个next都指向同一个地址,然后第二轮执行result->val=val%10的时候,其实导致整个result的每一个next的val值都发生改变。
测试用例: 342+465
第一轮: 刚执行完result->val=val%10
我们可以看到result->val=7,result->next->val中为空,二者存储的地址不同。
第一轮: 刚执行完result->next=result
我们会发现result中val值均为7,而且地址也都一样。
第二轮: 刚执行完result->val=val%10
因为指向的空间是一样的,所以这一次就同时变为4+6=10,取个位数为0。
第二轮: 刚执行完result->next=result
因为地址已经是一样的,没有变,所以这一句从第二轮开始已经没有效果了。
那错误的地方已经知道了,要怎么改呢?先来看看正确的代码是怎么样的。
这是正确的代码。是我根据分享的那篇博客里单链表的尾插法来改的。
执行用时:12ms
排名:超过 81% 提交记录
struct ListNode *addTwoNumbers(struct ListNode *l1, struct ListNode *l2)
{
int add = 0;
struct ListNode *result;
struct ListNode *p;//新建一个结点指针
struct ListNode *r;//再建立一个尾指针
result = malloc(sizeof(struct ListNode));
memset(result,0,sizeof(struct ListNode));
r=result;//很重要的一步,令尾指针r指向头结点l,便于做尾插入
while (l1 || l2 || add)
{
if (l1)
{
add += l1->val;
l1 = l1->next;
}
if (l2)
{
add += l2->val;
l2 = l2->next;
}
p=malloc(sizeof(struct ListNode)) ;//建立一个新结点
memset(p,0,sizeof(struct ListNode));
p->val = add % 10;
add = add / 10;
r->next = p;//尾指针的指针域指向新结点p
r=p;//再让尾指针放在p后面,尾指针永远在最后
}
result = result->next;
return result;
}
测试用例: 642+465
第一轮: 执行r = result
使得二者指向同一个空间。
第一轮: 刚执行完r->next = p
因为r和result二者指向同一个空间,所以给r->next赋值,同时给result->next赋值,所以我们可以发现r->next->val == result->next->val == p->val == 7。
第一轮: 刚执行完r = p
这一步我一开始没有理解。但我们来看r,p,result中存的内容就比较好理解。result->next指向的空间和r是一致的,这就使得我们第二轮执行r->next = p时,可以实现result->next->next = p,第三轮时,同样是r->next = p,实现的是result->next->next->next = p。
第二轮: 刚执行完r->next = p
r->next->val = 0,而result->next->next->val的值我忘了展开,但是我们看二者的存储的地址空间是一样的,所以也是0,符合预期。
第二轮: 刚执行完r = p
第三轮: 刚执行完r->next = p
第三轮: 刚执行完r = p
第四轮: 刚执行完r->next = p
第四轮: 刚执行完r = p
至此结束循环。
while循环 反思
代码是成功执行了,但还有一些注意的的地方。
- 因为我们的代码是直接在l1和l2上直接操作的,代码运行后,l1和l2就被破坏了。这在许多情况里并不合适。所以我们要把l1,l2中的数据先复制一份,在复制上进行操作。
- 对于指针这些分配好空间后,要记得清0初始化。我代码里是用memset全部清0了,而如果没有memset这一句,那么在 p->val = add % 10; 这一句之后要记得加 p->next = NULL;
- 代码运行后result中头指针中是空值,所以要result = result->next,之后return result。
- 再附一份代码
执行用时:0ms
排名:超过 100% 提交记录
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* struct ListNode *next;
* };
*/
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
struct ListNode *lst1 = l1;
struct ListNode *lst2 = l2;
struct ListNode *head = (struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *temp = head;
int t = 0;
while (lst1 || lst2 || t) {
if (lst1) {
t += lst1->val;
lst1 = lst1->next;
}
if (lst2) {
t += lst2->val;
lst2 = lst2->next;
}
struct ListNode *NewNode = (struct ListNode*)malloc(sizeof(struct ListNode));
NewNode->val = t % 10;
NewNode->next = NULL;
temp->next = NewNode;
temp = temp->next;
t /= 10;
}
return head->next;
}
递归
写完代码后想再改成递归的形式。但是leetcode报错,而vscode中跑是可以跑,但是有警告。问题应该是说我的代码并不是任何一个分支都有返回的内容。
solution.c: In function ‘result’
Line 23: Char 1: error: control reaches end of non-void function [-Werror=return-type] [solution.c]
}
^
cc1: some warnings being treated as errors
以下是我的代码。代码中的return 0,如果注释掉就会报错,如果不注释掉就会返回空值。但我的理解是除非满足(!l1 && !l2 && !add)这个条件,否则不应该是永远递归吗?为什么会跑到return 0;这一步。
如果有大大知道该怎么改,烦请评论区告诉我吧!多谢啦!
struct ListNode *result(struct ListNode *l1, struct ListNode *l2, struct ListNode *ret,struct ListNode *r,int add){
if(!l1 && !l2 && !add){
ret=ret->next;
return ret;
}
if (l1)
{
add += l1->val;
l1 = l1->next;
}
if (l2)
{
add += l2->val;
l2 = l2->next;
}
struct ListNode *p=(struct ListNode*)malloc(sizeof(struct ListNode)) ;//建立一个新结点
memset(p,0,sizeof(struct ListNode));
p->val = add % 10;
r->next = p;
r=p;
result(l1,l2,ret,r,add/10);
// return 0;
}
struct ListNode *addTwoNumbers(struct ListNode *l1, struct ListNode *l2)
{
struct ListNode *ret=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *r=(struct ListNode*)malloc(sizeof(struct ListNode));
struct ListNode *r1=l1;
struct ListNode *r2=l2;
r=ret;
return result(r1,r2,ret,r,0);
}