LeetCode题目笔记--2.两数相加

2023-11-04

这个博客系列记录我刷LeetCode过程中的一些循序渐进的思路和想法,希望能坚持下去。如果读者老爷觉得有帮助,就点个赞吧。

题目描述

给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。

如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。

您可以假设除了数字 0 之外,这两个数都不会以 0 开头。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/add-two-numbers著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807

题目解读及思路

这个题考的是最简单的单链表的使用。题目已经说明了链表是非空的且表示的整数非负,那么在考虑过程中就可以少考虑这两点了。
需要注意的几点:
1.两数的位数相同,最后结果不用进位;
2.位数相同,最后结果需进位;
3.位数不同,最后结果需进位。
当然,算法好的话,实现时根本不用单独考虑这几点。

思路1

创建一个结果链表头指针result,一个指向新结点的指针pr,两个数据指针p1p2分别指向两个单链表。
在循环体里,先判断p1和p2是否都到表尾了,是则退出循环,否则执行加法操作,创建结果结点,设置进位标志。
python代码:

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        result = ListNode(0)
        pr = result
        p1, p2 = l1, l2
        carryIn = 0 #进位标志
        preNode = pr #前导结点指针
        while True:
            if p1 == None and p2 == None: #p1和p2都完了,退出循环
                break
            if pr == None:	#pr为空,先创建结果结点
                pr = ListNode(0)
                preNode.next = pr
                preNode = pr

            if p1:
                pr.val += p1.val
            
            if p2:
                pr.val += p2.val
            
            pr.val += carryIn 
            carryIn = 1 if pr.val > 9 else 0
            pr.val %= 10
            if p1:  # 后移p1、p2
                p1 = p1.next
            if p2:
                p2 = p2.next
            preNode = pr
            pr = pr.next
        
        if carryIn:	#都加完了后,还有进位信号
            pr = ListNode(1)
            preNode.next = pr
        return result

这个代码的运行时间是100ms,只超过了11.03%的人,肯定还有改进的地方。

思路1改进

上面的代码中,使用了pr、preNode两个指针来生成结果链表,这是因为一开始我想的是第一个结点就开始存结果,而且因为python对象的特性,不能只用一个pr来创建这个链表。这样做的话,就带来了很多麻烦,循环体中的

if pr == None:	#pr为空,先创建结果结点
      pr = ListNode(0)
      preNode.next = pr
      preNode = pr

除了第一次循环,pr为假,后面一直都是真,这就导致效率低下。所以,将结果链表第一个结点作“头结点”,这样每次循环都不用判断pr了,直接为pr创建一个新的结点。
其次,再观察代码,循环体中前后有两处判断p1和p2是否为空,在移动p1、p2前,同一个循环体中判断结果肯定相同,理所当然的应该将它们分别合并。
再次,再观察代码,carryIn信号只取01是不是效率很低?为什么不直接把这个信号同时作进位和加法结果用?同时,循环体完后的那个if代码块,也可以加进循环体中。于是,改进后的代码如下:

class Solution:
    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
        pr = result = ListNode(0)
        carryIn = 0
        while l1 or l2 or carryIn:
            carryIn += (l1.val if l1 else 0) + (l2.val if l2 else 0) 
            pr.next = ListNode(carryIn % 10)
            pr = pr.next
            carryIn //= 10 # 除以10,再向下取整,0或1
            l1 = l1.next if l1 else None
            l2 = l2.next if l2 else None
            
        return result.next

这个代码的运行时间是68ms,战胜了90.67%的人,勉强还行吧(强行鼓励自己一下),之余更快的40ms和50ms档的人,可能是他们的代码写的更贴合硬件,所以更快。

C语言代码

温故而知新,好久没用过C了,用C实现了一下,也是折腾了好久,才改完错误。

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
    struct ListNode *head, *ph, *pre;
    pre = (struct ListNode*) malloc (sizeof(struct ListNode));
    head = pre;
    head->next = NULL;
    int carryIn = 0;
    while(l1 != NULL || l2 != NULL || carryIn)
    {
        if (l1 != NULL)
        {
            carryIn += l1->val;
			l1 = l1->next;
        }
        if (l2 != NULL)
        {
            carryIn += l2->val;
			l2 = l2->next;
        }
        ph = (struct ListNode*) malloc (sizeof(struct ListNode));
        ph->val = carryIn % 10;
        ph->next = NULL;
        pre->next = ph;
        pre = ph;
        carryIn = floor(carryIn / 10.0f);
    }
    return head->next;
}

这个C代码运行时间是36ms,确实比python快多了,但也是垫底,再观察代码,肯定还有改进的地方。
1.循环体中最后一句用了floor函数,可以直接除以10,即
carryIn /= 10,反正整数相除也是类似向下取整的机制。
2.循环倒数第三句,没必要每次循环都执行,反正下一次循环这个next会被赋值,所以可以把这一句拿出来在循环完之后执行,收尾。
改进之后:

struct ListNode* addTwoNumbers(struct ListNode* l1, struct ListNode* l2){
    struct ListNode *head, *ph, *pre;
    pre = (struct ListNode*) malloc (sizeof(struct ListNode));
    head = pre;
    head->next = NULL;
    int carryIn = 0;
    while(l1 != NULL || l2 != NULL || carryIn)
    {
        if (l1 != NULL)
        {
            carryIn += l1->val;
			l1 = l1->next;
        }
        if (l2 != NULL)
        {
            carryIn += l2->val;
			l2 = l2->next;
        }
        ph = (struct ListNode*) malloc (sizeof(struct ListNode));
        ph->val = carryIn % 10;
        pre->next = ph;
        pre = ph;
        carryIn /= 10;
    }
	ph->next = NULL;
    return head->next;
}

改进后执行时间是16ms,提升了20ms。

如果链表不是逆序,是正着来怎么办?

这样的话,我的初步想法是先把链表中的元素分别按序入栈,然后在循环体中用这两个栈来进行操作。这样其实也就多了前面入栈的开销,循环体中出栈和指针后移开销应该差不多。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

LeetCode题目笔记--2.两数相加 的相关文章

随机推荐

  • 通过Maven命令创建Web项目

    1 创建Web项目 mvn archetype create DgroupId com demo 项目组标识 DartifactId omss 项目名称 DarchetypeArtifactId maven archetype webapp
  • 火爆全网的chat GPT 在煤矿智能问答方面的应用

    测试了19个煤矿智能化 综采方面的问题 甚至会自己写代码 看看chatGPT表现如何 什么是智能化煤矿 什么是记忆割煤 目前记忆割煤都存在哪些问题 煤矿数字孪生技术可以用哪些开源的应用来实现 智能化煤矿未来可以发展到什么程度 提供煤矿智能化
  • git仓库规范

    多人协作 项目名称 demo 我的名字 kk 1 前置概念 主目录 develop 开发目录 dev 主分支 develop demo 开发分支 dev demo kk 2 主目录 develop 该目录下可以有很多项目的分支 dev目录下
  • AI三大主义:符号主义、联结主义、行为主义

    一 符号主义 symbolicism 符号主义 symbolicism 逻辑主义 Logicism 心理学派 Psychlogism 计算机学派 Computerism 其原理主要为物理符号系统 即符号操作系统 假设和有限合理性原理 早期的
  • 【C#基础详解】(十四)面向对象 继承

    面向过程 优点 性能比面向对象高 因为类调用时需要实例化 开销比较大 比较消耗资源 比如单片机 嵌入式开发 Linux Unix等一般采用面向过程开发 性能是最重要的因素 缺点 没有面向对象易维护 易复用 易扩展 面向对象 面向对象的三个核
  • Zabbix安装时出现缺少PHP模块,解决过程

    我在安装时PHP缺少gettext模块和bcmath模块 一下为解决步骤 1 进入到PHP源码包目录下的ext目录 cd soft php 5 3 13 ext 2 会看到ext目录下有gettext目录和bcmath目录 3 进入gett
  • 对称二叉树

    这是蒟蒻认真写的第一篇题解 如有欠缺 请理解 题目描述 一棵有点权的有根树如果满足以下条件 则被轩轩称为对称二叉树 1 二叉树 2 将这棵树所有节点的左右子树交换 新树和原树对应位置的结构相同且点权相等 下图中节点内的数字为权值 节点外的
  • 下载google code中源码的几个工具

    Google code 一般以三种命令行方式提供源代码 格式如下 plain view plain copy hg clone https code google com p xxx git clone https code google
  • redis中批量删除key

    1 删除所有的key 可以使用redis自身的命令 flushdb 删除当前数据库中的所有Key flushall 删除所有数据库中的key 2 使用linux中的xargs来删除所有的key redis cli keys xargs re
  • 【R】【线性回归分析实验】

    文章目录 实验思维导图 1 收集 探索和准备数据 1 1 收集数据 1 2 探索和准备数据 2 基于数据训练模型 2 1 使用线性回归函数 2 2 建立模型 3 评估模型的性能 4 提高模型的性能 4 1 将年龄非线性化 4 2 数值转换二
  • 愉快地使用你的 Git Bash 工具

    在windows下使用git时自然会用到Git Bash 下面我分享一些Git Bash的使用技巧 欢迎补充 官方下载地址 http msysgit github io 设置初始路径 默认的 Git Bash 初始路径为安装目录 每次打开都
  • mvc三层架构

    三层架构是指 视图层View 业务逻辑层Service 持久层DAO View层 用于接收用户提交请求的代码 Service层 系统的业务逻辑主要在这里完成 DAO层 直接操作数据库的代码 主要是做数据持久层的工作 扩展 MVC指MVC模式
  • MySql 及MyBatis数据的批量操作

    1 Mybatis操作 1 批量更新
  • python hashlib_python import hashlib出现问题

    import hashlib时出现如下问题 gt gt gt import hashlib ERROR root code for hash md5 was not found Traceback most recent call last
  • ubuntu安装向日葵报错 处理时遇到错误:/var/cache/apt/archives/apport_2.20.1-0ubuntu2.4_all.deb

    执行安装命令 sudo dpkg i sunloginclient deb后 可能会报错 在处理时有错误发生 sunloginclient 此时执行 sudo apt get install f y 然后重新安装即可 但按以上方法操作后不一
  • RSA进阶之维纳攻击(wiener-attack )

    维纳攻击 场景 e很大 例题 第七届山东网络安全技能大赛 链接 https pan baidu com s 1IRInw3pB7SQfp3MxRJW17A 提取码 lcn3 e很大 妥了 维纳攻击 脚本在github上 https gith
  • 【完全开源】小安派-DSL 屏幕驱动开发板

    文章目录 概述 系统框图 2 8 3 5寸 屏电路 2 4寸触摸屏电路 1 28 寸圆形触摸屏电路 背光控制 关于Demo 1 28寸圆形屏智能手表Demo 2 4寸屏音乐播放器Demo 3 5寸屏天气站Demo 完全开源 概述 小安派 D
  • Altium Designer导入元器件3D封装

    一 前言 AD用了也有几年了 一开始只是单独用于制版 没有别的用途 随着工龄的增长 需求的内容也是越来越多 逐渐接触了3D模型建立 结构设计 有时需要导入PCB 3D效果 发现PCB导出的大多数只有芯片和电阻电容 很多开关 端子等特殊封装的
  • 前端开发利器: Bootstrap + AngularJS

    概述 在HTML5盛行的互联网时代 涌现诸多的前端html css js框架 基于其 适用范围 licence 发展前景等因素 本人对比总结出其中的两个佼佼者 分别是侧重页面美化展现的 Bootstrap 和侧重页面逻辑控制的 Angula
  • LeetCode题目笔记--2.两数相加

    这个博客系列记录我刷LeetCode过程中的一些循序渐进的思路和想法 希望能坚持下去 如果读者老爷觉得有帮助 就点个赞吧 题目描述 给出两个 非空 的链表用来表示两个非负的整数 其中 它们各自的位数是按照 逆序 的方式存储的 并且它们的每个