当前位置: 首页 > news >正文

复习leetcode第二题:两数相加

本文会给出笔者自己的解答(代码较为冗余,其实就是屎山代码)以及优秀代码的解析

下图是题目

b98ab95f90244322a1a09ac50efca748.png

解法1(笔者所使用的办法): 

解题思路:

以下思路是基于示例1(上图)思考的

步骤1:因为该函数只传来了两个链表的首元结点指针,所以我们不难想到可以创建一个新链表来存放两链表相加的内容

步骤2:由于我们最后需要返回新链表的首元结点指针,而新链表不断创建以后,用于创建链表的指针也后移了,因此我们还需要创建一个指针phead,用作最后的函数返回

步骤3:题目给我们的结构体名称为Listnode(在注释行写了),笔者觉得大小写切换太麻烦了,因此这边改成了listnode

步骤4:通过示例1我们也不难发现,我们需要使用循环语句来多次让两个链表的结点内容相加,并存放到新链表中;而循环的退出条件应该是l1链表和l2链表的所有结点的数据全部相加完了,即l1、l2同时为空指针

上述步骤代码如下:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode listnode;
listnode* buynode(int x) //用于创建新链表的函数{listnode* newnode = (listnode*)malloc(sizeof(listnode));newnode->val = x;newnode->next = NULL;return newnode;
}//l1首元结点指针      //l2首元结点指针
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) 
{int count=0;listnode* newnode=buynode(0); //创建一个新链表的首元结点,之后每次创建新链表都传0值,因为只有给新链表结点0这个初值才不会影响结果//当然也并不是必须给新链表的结点赋初值,这里只是为了保险起见listnode* phead=newnode;while(l1||l2) //退出条件为两个指针全为空指针{……;}return phead;
}

1f08a6952a6c4ca58a8f0183ef310a4c.png

示例1的情况:

示例1中出现了两数相加等于10的情况,最后l1、l2结点所对应的新链表结点留下来的数据为0,然后把 1这个数值 进1位和后续结点数据内容相加,这也就导致了 4+3+1 = 8。但我们不难发现,l1、l2的结点数据相加最大值为18(即使加上1也只有19),因此只有可能把1这个数值进1位,不可能把1以外的数值进1位。 

因此我们可以进行一个分支语句,分为了最后l1、l2结点数据相加 大于等于10 小于等于9两种情况;然后通过一个计数器count,来判断是否需要对后一个结点数据加1

每次相加完,让l1、l2、新链表都往后移动一位

代码如下所示:

 

        //分为l1+l2 <=9 以及 l1+l2 >=10 两种情况//<=9if((l1->val)+(l2->val)+count<=9) {newnode->val=(l1->val)+(l2->val)+count;count=0;}//>=10else{newnode->val=(l1->val)+(l2->val)+count-10;count=1;}l1=l1->next;l2=l2->next; newnode->next=buynode(0);newnode=newnode->next;

bcd5c9e8d2374ad2af6f20353d02538e.png

示例2的情况用示例1的代码就能解决,此处不再讲解。

f6607e7e89c5422dbc8b24a36c825832.png

示例3的情况:

该示例告诉我们,l1为空时,l2不一定为空;l2为空时,l1不一定为空。

因此,会有三种情况:分别是l1、l2都不为空,只有l1为空,只有l2为空。此处可以通过三条分支语句来解决。

当l1为空,l2不为空时,把l1继续往后移动一位代码会出错(对空指针进行解引用操作),因此我们需要在不同的分支语句里面对不同情况进行不同的向后移位操作

而当把所有数据相加完,l1、l2都为空的时候,有可能count仍为1(示例3输出里最后会出现一个1的原因),因此我们需要在整个循环语句后,解决这个问题。直接在新链表最后面加上一个结点,且最后一个结点数据域只可能为1(上文已经讲解过只可能为1的原因)

代码如下所示:

 

        if(l1&&l2) //两指针都非空{//分为l1+l2 <=9 以及 l1+l2 >=10 两种情况//<=9if((l1->val)+(l2->val)+count<=9) {newnode->val=(l1->val)+(l2->val)+count;count=0;}//>=10else{newnode->val=(l1->val)+(l2->val)+count-10;count=1;}l1=l1->next;l2=l2->next; }else if(l1) //l1还不为空的情况{if((l1->val)+count<=9){newnode->val=(l1->val)+count;count=0;}else{newnode->val=(l1->val)+count-10;count=1;}l1=l1->next; //为防止对空指针l2进行解引用操作}    else if(l2) //l2还不为空的情况{if((l2->val)+count<=9){newnode->val=(l2->val)+count;count=0;}else{newnode->val=(l2->val)+count-10;count=1;}l2=l2->next; //为防止对空指针l1进行解引用操作}  if(count==1) //两个指针全都为空但count还是为1,是对示例3的解决{newnode->next=buynode(1); //直接在newnode指针最后面加上一个结点,且最后一个结点数据域只可能为1}  

由于新链表一直要创建到l1、l2都为空,那么在循环语句(循环语句退出条件为l1、l2都为空)里的新链表创建就不需要加以限制了呢?

答:如果不加以限制,会出现下图的情况,这是由于在最后一次l1、l2结点数据相加并放入新链表以后,还会再进行一次新链表的结点创建

解决方法:

  1. 把新链表的首元结点的创建放在循环语句里面,并且在第一次创建新链表的结点时,把该结点赋给phead,并且所有操作都放在l1、l2结点数据相加之前
  2. 在循环语句末尾,给新链表的创建加上一条if语句,当l1、l2全为空指针,不再进行结点创建工作(笔者在此使用的)

4a886befb54f41dcb16ffdaa555f483c.png

        if(l1||l2) //只有两指针还有需要存入的数据再开辟新的空间,如果都已经存放完毕,那么就无需再开辟新的{newnode->next=buynode(0);newnode=newnode->next;}

解法1全部代码展示:

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/typedef struct ListNode listnode;
listnode* buynode(int x){listnode* newnode = (listnode*)malloc(sizeof(listnode));newnode->val = x;newnode->next = NULL;return newnode;
}//l1首元结点指针      //l2首元结点指针
struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) 
{int count=0;listnode* newnode=buynode(0); //创建一个新链表的首元结点listnode* phead=newnode;while(l1||l2) //退出条件为两个指针全为空{if(l1&&l2) //两指针都非空{//分为l1+l2 <=9 以及 l1+l2 >=10 两种情况//<=9if((l1->val)+(l2->val)+count<=9) {newnode->val=(l1->val)+(l2->val)+count;count=0;}//>=10else{newnode->val=(l1->val)+(l2->val)+count-10;count=1;}l1=l1->next;l2=l2->next; }else if(l1) //l1还不为空的情况{if((l1->val)+count<=9){newnode->val=(l1->val)+count;count=0;}else{newnode->val=(l1->val)+count-10;count=1;}l1=l1->next; //为防止对空指针l2进行解引用操作}    else if(l2) //l2还不为空的情况{if((l2->val)+count<=9){newnode->val=(l2->val)+count;count=0;}else{newnode->val=(l2->val)+count-10;count=1;}l2=l2->next; //为防止对空指针l1进行解引用操作}    if(l1||l2) //只有两指针还有需要存入的数据再开辟新的空间,如果都已经存放完毕,那么就无需再开辟新的{newnode->next=buynode(0);newnode=newnode->next;}}if(count==1) //两个指针全都为空但count还是为1,是对示例3的解决{newnode->next=buynode(1); //直接在newnode指针最后面加上一个结点,且最后一个结点数据域只可能为1}return phead;
}

1eb04a4521f04ef3be2c8e8d44f01e2a.png


解法2(优秀代码):
 

下面的代码是leetcode官方给的C语言题解,好漂亮的代码!

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2) {struct ListNode *head = NULL, *tail = NULL;int carry = 0;while (l1 || l2) {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;}}if (carry > 0) {tail->next = malloc(sizeof(struct ListNode));tail->next->val = carry;tail->next->next = NULL;}return head;
}

上述代码解释:

官方题解也是通过创建一个新链表,然后解决问题

首先创建了两个指针,一个指向了首元结点(用作返回),一个指向了尾元结点(用作存放数据);carry和我代码中的count一样,都是保存多出来的那个1的

循环语句退出条件、分支语句的写法此处省略

l1 ? l1->val : 0 --->该操作符的作用:l1不为空指针,就留下l1的值;l1为空指针,就留下0

l2 ? l2->val : 0 --->作用同上

sum:就是l1、l2的结点数据(还有carry)相加后结果

!head:如果头指针为空指针为真(首元结点的创建,和首元结点地址的保留)

sum%10:对10取余

sum/10:整型相除

进行完对新链表的赋值操作以后,让新链表的尾元结点指针指向空指针,等待下一次使用

最后如果carry依旧为1,那么再开辟一个结点空间存放,最后返回head指针

 

 

相关文章:

  • Pytorch入门需要达到的效果
  • 【教学类-60-01】彩色消划掉01(四个数字,X*Y宫格)
  • Linux - 文件管理高级1
  • 2.4 Docker部署JDK
  • 【三维模型采集设备】轮廓扫描仪介绍
  • TensorFlow Playground神经网络演示工具使用方法详解
  • golang中一个优雅的开发和使用命令行工具的库 cobra
  • CraftCMS ConditionsController.php 代码执行漏洞(CVE-2023-41892)
  • 【算法训练 day44 分割等和子集】
  • Mysql 插入或者更新 踩坑
  • QT系列教程(6) 几种标准对话框
  • ReactNative集成到已有iOS项目
  • 大模型日报2024-05-31
  • C++:vector的模拟实现
  • Maven 中的 classifier 属性用过没?
  • js
  • MySQL数据库运维之数据恢复
  • Rancher如何对接Ceph-RBD块存储
  • Solarized Scheme
  • TCP拥塞控制
  • vue2.0项目引入element-ui
  • 分享一个自己写的基于canvas的原生js图片爆炸插件
  • 干货 | 以太坊Mist负责人教你建立无服务器应用
  • 高度不固定时垂直居中
  • 开年巨制!千人千面回放技术让你“看到”Flutter用户侧问题
  • 名企6年Java程序员的工作总结,写给在迷茫中的你!
  • 远离DoS攻击 Windows Server 2016发布DNS政策
  • 做一名精致的JavaScripter 01:JavaScript简介
  • 看到一个关于网页设计的文章分享过来!大家看看!
  • hi-nginx-1.3.4编译安装
  • 阿里云移动端播放器高级功能介绍
  • 如何在 Intellij IDEA 更高效地将应用部署到容器服务 Kubernetes ...
  • ​决定德拉瓦州地区版图的关键历史事件
  • ![CDATA[ ]] 是什么东东
  • "无招胜有招"nbsp;史上最全的互…
  • #Z0458. 树的中心2
  • (06)金属布线——为半导体注入生命的连接
  • (10)STL算法之搜索(二) 二分查找
  • (4)STL算法之比较
  • (52)只出现一次的数字III
  • (day 12)JavaScript学习笔记(数组3)
  • (回溯) LeetCode 131. 分割回文串
  • (七)Activiti-modeler中文支持
  • (七)Knockout 创建自定义绑定
  • (深入.Net平台的软件系统分层开发).第一章.上机练习.20170424
  • (转)linux下的时间函数使用
  • (总结)Linux下的暴力密码在线破解工具Hydra详解
  • . ./ bash dash source 这五种执行shell脚本方式 区别
  • .config、Kconfig、***_defconfig之间的关系和工作原理
  • .htaccess配置常用技巧
  • .NET MVC 验证码
  • .NET(C#) Internals: as a developer, .net framework in my eyes
  • .net6+aspose.words导出word并转pdf
  • .Net的C#语言取月份数值对应的MonthName值
  • .net和php怎么连接,php和apache之间如何连接