LeetCode 热题 HOT 100(01,两数相加)

不够优秀,发量尚多,千锤百炼,方可成佛。

算法的重要性不言而喻,无论你是研究者,还是最近比较火热的IT 打工人,都理应需要一定的算法能力,这也是面试的必备环节,算法功底的展示往往能让面试官眼前一亮,这也是在大多数竞争者中脱颖而出的重要影响因素。

然而往往大多数人比较注重自身的实操能力,着重于对功能的实现,却忽视了对算法能力的提高。有的时候采用不同的算法来解决同一个问题,运行效率相差还是挺大的,毕竟我们最终还是需要站在客户的角度思考问题嘛,能给用户带来更加极致的体验当然再好不过了。

万法皆空,因果不空。Taoye之前也不怎么情愿花费太多的时间放在算法上,算法功底也是相当的薄弱。这不,进入到了一个新的学习阶段,面对导师的各种“严刑拷打”和与身边人的对比,才开始意识到自己“菜”的事实。

讲到这,流下了没技术的眼泪!!!

这次的题目是LeeTCode 热题 HOT 100的第二题,难度属于中等,涉及到了链表的知识。

自打接触Python以来,都没有从中用到过链表,也无法通过指针来操作链表。曾经也只是在备考408,学习C的过程中刷过一些链表相关算法,一开始拿到这道题的时候,不知道Python如何下手,不知道怎么操作链表,菜是原罪(ノへ ̄、)

查找资料之后才发现,我们操作链表的时候其实就是将其封装成一个实例对象,而实例对象中存储了节点的相关信息(其实和C中差不多)。但与C或C++中不同的是,Python没有指针,也就没有什么指向操作,所以从对链表的整体结构理解上,Python和C、C++还是有点区别的。意思类似下图:

可以理解成一种俄罗斯套娃的形式。

注意:只是理解上有所区别,其实表达的意思还是一样的。

下面,我们就来看看这道题吧。

题目:两数相加

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

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

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

示例

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

思路

前面也提到了,在Python中,链表可以理解成一种套娃的形式,通过输入两个链表,输出一个新的链表,新链表中的每个节点的产生过程基本一致,所以我们初步判断通过递归的方法来解决该问题。

当然了,递归能解决的问题,非递归同样可以,只是将一次递归函数的调用替换成一次循环。所以下面将分别介绍两种方法。

  • 方法一:递归

题中给出的例子里的两个链表的长度是一样的,但题意所表明的意思是任意长度的两个链表。所以,为了提高算法的“泛化性能”,在输入两个长度不同链表的前提下,我们需要对其进行一个统一,就是对长度短的链表我们需要做一个补0处理。补0并不影响求和操作,但又方便了我们对截止递归的判断。

这种处理方式在数学上还是挺常见的,比如说在求最值的时候,我们常常会用到均值不等式,但题目中给出的数值维数可能并不会明显地满足均值不能式,所以常常会用到一个扩维的操作,很巧妙的将题目向均值不等式靠近,从而简化了对题目的解答,比如下面这道题的巧妙解答:

最后,左右同开5次方,再同乘27即可证毕。

回到算法题吧。

题目既然要我们求链表所对应的单位数之和,那么肯定会涉及进位,我们不妨将进位表达为carry。至此,我们不难想到,每次的递归需要的参数有三个,分别是第一个链表中的值、第二个链表中的值、上一次进位的值(非0即1)。

前面也有说到,在Python中,链表其实就是一个对象,而该对象中又有俩个属性,分别是valnext,而next本身有代表一个链表对象。所以我们递归方法传入的第一、第二个参数可以用当前链表节点所获取到。

判断跳出递归的条件:前面,我们已经对链表进行处理过了,也就是说无论你输入的两个链表长度是否一致,我们都对其进行了统一长度的处理。所以说当我们所传入的三个参数同时为False的时候即可跳出递归。举例说明: 1、输入两个空链表和一个carry为1的参数,说明已经产生了进位,此时我们需要对其进行处理,而非跳出递归。2、输入的一个链表节点value值为5,而另一个节点为None,carry为0,此时我们依然需要进行求和处理,即使另一个节点为None,但我们已经对其进行了补0处理。3、至于其他可能性,各位看官可自行思考。

求和处理:

  1. 求出两个链表当前value值和carry的和,并对10进行一个divmod操作。说明: Python中divmod会返回一个元组,第一个值为进位,第二个值为取余,比如divmod(13, 10) = (1, 3)。注意:在求和的过程中,有可能当前节点为None,这个时候则需要将节点的value值作为0进行求和。

  2. 实例化一个链表,也就是我们的目标返回链表,其value值为上述的取余。注意:这里一定理解清楚题意,链表中的值是实际值的逆序。

  3. result的next属性所对应的值本身又是一个链表,而对其进行赋值的时候,我们需要调用递归方法。传递的三个参数:经过上述过程,链表中的当前值已经处理完毕,这个时候需要处理链表的下一个值,也就是将下一个节点作为参数进行一次递归。但这里需要考虑的是,假如我们的当前节点为空,也就是说当前链表已经遍历结束,则需要传递None给递归函数。

相关代码:

class Solution:    def recursion(self, list_node1, list_node2, carry):        # 判断跳出递归的条件        if (list_node1 == None) and (list_node2 == None) and (carry == 0): return None        # 两个节点值和进位值的求和操作,若传入的节点为None,则需要将value赋值为0进行求和,也就是补0        sum_number = (list_node1.val if list_node1 else 0) + (list_node2.val if list_node2 else 0) + carry        # 产生新的carry进位值,作为下一次递归的判断。        carry, value = divmod(sum_number, 10)        # 实例新的节点(result,为返回的目标节点的),其val属性为value        result = ListNode(value)            # 进行下一次递归        result.next = self.recursion(list_node1.next if list_node1 else None, list_node2.next if list_node2 else None, carry)        return result

    # 主函数    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:        return self.recursion(l1, l2, 0)    # 执行入口,调用递归函数,传入初试的两个节点,默认进位为0
  • 方法二:非递归

非递归的思想和递归差不多,只是除了返回result链表之外,还需要额外创建一个新的链表用于循环。

class Solution:    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:        # 初始node,result为返回结果链表,temp_node用作循环遍历,链接产生新的节点        result = temp_node = ListNode(0)            carry = 0   # 初始进位为0        while l1 or l2 or carry:    # 跳出循环的条件和递归一样,三者同时为False的时候跳出循环            # 两个节点值和进位值的求和操作,若传入的节点为None,则需要将value赋值为0进行求和,也就是补0            sum_number = (l1.val if l1 else 0) + (l2.val if l2 else 0) + carry            # l1和l2当前节点操作完毕,指向下一个节点,准备进入下一次循环            l1 = l1.next if l1 else None; l2 = l2.next if l2 else None            # 产生新的carry进位值,作为下一次递归的判断。            carry, value = divmod(sum_number, 10)            # 重新赋值temp_node节点,不断为result链表进行延伸补充节点            temp_node.next = ListNode(value); temp_node = temp_node.next        return result.next

总的来说,非递归表达的思想和递归差不多。

不过这里有一点值得说明下: result节点只在初始和最后返回时用到过,而在算法的核心while循环中并没有用到,为什么还能返回正常的result链表呢?

这主要是因为,我们初始化创建的result已经和temp_node都赋值给新的链表了,其中val为0,next属性值为None,其内存位置是固定的。而初试的result和temp_node是相等的,所指向的内存地址是一样的,所以我们只需要的延伸temp_node节点即可,而result地址不变,而result指向的下一个节点的地址则会随着temp_node的变化而变化。

注意: result的内存地址从始至终都是不变的,初试的时候和temp_node的地址是一样,只不过之后变化的是temp_node,而result不变。我们可以在通过如下操作简单测试下这个过程:

也不知道自己表述清楚了没有?

也不知道各位看官理解了没有?

如果没看懂的话,强烈建议重新看一遍,细细琢磨下,这个地方还是挺重要的。

  • 最后

在逛讨论区的时候,看到了另外一种解法,感觉这算法写的挺优雅的,在这里分享下。

"""    Author:学废了的Kai"""class Solution:    def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:        head = curr = ListNode()        carry = val = 0        while carry or l1 or l2:            val = carry            if l1: l1, val = l1.next, l1.val + val            if l2: l2, val = l2.next, l2.val + val            carry, val = divmod(val, 10)            curr.next = curr = ListNode(val)        return head.next

这种解法和上面非递归表达的是同种思想,只不过在求sum的时候这个是叠加的形式,而非一次性求和。上面算法还有一点值得学习的是,在最后curr.next = curr = ListNode(val)这行代码中,其表达的意思是如下:

curr.next =ListNode(val)curr = curr.next

Taoye也不知道为什么是这种赋值顺序,按道理讲应该是先赋值curr = ListNode(val),再赋值curr.next = curr?但经过测试之后,上述两行代码块才是正确的,暂时不知道为什么,之后有机会再回过头看看吧。

我是Taoye,研究生在读。爱专研,爱分享,热衷于各种技术,学习之余喜欢下象棋、听音乐、聊动漫,希望借此一亩三分地记录自己的成长过程以及生活点滴,也希望能结实更多志同道合的圈内朋友,更多内容欢迎来访微信公主号:玩世不恭的Coder

推荐阅读:

LeetCode 热题 HOT 100(00,两数之和)
Taoye渗透到一家黑平台总部,背后的真相细思极恐
《大话数据库》-SQL语句执行时,底层究竟做了什么小动作?
那些年,我们玩过的Git,真香
基于Ubuntu+Python+Tensorflow+Jupyter notebook搭建深度学习环境
网络爬虫之页面花式解析
手握手带你了解Docker容器技术
一文详解Hexo+Github小白建站
打开ElasticSearch、kibana、logstash的正确方式

两数相加c++_LeetCode 热题 HOT 100(01,两数相加)相关推荐

  1. c语言判断字符串是不是回文_LeetCode 热题 HOT 100 5. 最长回文子串

    题目 题解 暴力法 我们根据回文字符串特点进行判断一个字符串是不是回文. // 回文子串:首尾对称相等const isPalindrome = s => { // abba aba for (l ...

  2. [ 热题 HOT 100] ---15.三数之和---排序+双指针

    1 题目描述 给你一个包含 n 个整数的数组 nums,判断 nums 中是否存在三个元素 a,b,c ,使得 a + b + c = 0 ?请你找出所有满足条件且不重复的三元组. 注意:答案中不可以 ...

  3. Leetcode python《热题 HOT 100》1. 两数之和

    Leetcode python 之 <热题 HOT 100>:https://leetcode-cn.com/problemset/hot-100/ 1. 两数之和 给定一个整数数组 nu ...

  4. Leetcode python《热题 HOT 100》15. 三数之和

    Leetcode python 之 <热题 HOT 100>:https://leetcode-cn.com/problemset/hot-100/ 15. 三数之和 给定一个包含 n 个 ...

  5. LeetCode 热题 HOT 100

    LeetCode 热题 HOT 100之JAVA解法 第1题 两数之和(简单) 代码 第2题 两数相加(中等) 代码 第3题 无重复字符的最长子串(中等) 代码 第4题 寻找两个正序数组的中位数(困难 ...

  6. 《LeetCode 热题 HOT 100》Java答案汇总版---持续更新中

    <LeetCode 热题 HOT 100>Java答案汇总版-持续更新中 个人认为<LeetCode 热题 HOT 100>中的题目特别适合算法新手进行一个入门的刷题,而且作者 ...

  7. Leecode01. 两数之和——Leecode大厂热题100道系列

    我是小张同学,立志用最简洁的代码做最高效的表达 以下是我个人做的题解,每个题都尽量囊括了所有解法,并做到了最优解,欢迎大家收藏!留言! 传送门-->Leecode大厂热题100道系列题解 给定一 ...

  8. Leecode15. 三数之和——Leecode大厂热题100道系列

    我是小张同学,立志用最简洁的代码做最高效的表达 以下是我个人做的题解,每个题都尽量囊括了所有解法,并做到了最优解,欢迎大家收藏!留言! 传送门-->Leecode大厂热题100道系列题解 问题描 ...

  9. LeetCode 热题 HOT 100 完整题解笔记知识点分类 C++代码实现

    1.知识点分布 填一下这个之前欠的天坑,复习一下算法入门的经典基础题. 除夕,正月初一,初二,一共写了三整天,除了吃饭就窝着补题. 每天30题+,整个人都写晕啦,终于写完啦() markdown生成 ...

最新文章

  1. 新康众闫顺成:数据中台建设中的得与失
  2. Javascript学习笔记12——Ajax入门
  3. Java的二十三种设计模式(原型模式(Prototype))
  4. 读书印记 - 《创新者的解答》
  5. pyqt 取鼠标处文字_爱剪辑:炫彩的动态标题文字特效,这招让视频片头LOGO更酷炫...
  6. 从零开始学习音视频编程技术(二) 音频格式讲解
  7. (三)使用Keras构建移动风格迁移CycleGAN
  8. python电影院售票系统毕业设计开题报告
  9. 皮尔逊相关系数和斯皮尔曼相关系数
  10. 怎么在mac下运行映像dmg_如何将Apple Mac OS X系统DMG镜像文件转换成ISO镜像文件呢?...
  11. python注册大漠插件,大漠插件安装包
  12. Android如何实现全局的护眼模式
  13. Python3.9.10标准库与语言参考等文档下载
  14. php微信绑定银行卡_微信开发企业支付到银行卡PHP
  15. 【支付宝小程序控制硬件①】 申请个人支付宝小程序开发的个人账户,说说那些睬坑日志,集成mqtt协议在支付宝小程序,实现基本通讯!
  16. 微信服务商子商户支付
  17. php+fastcgi+apache2+php-fpm配置
  18. 将无处不在的网络爬虫引向合法的轨道已迫在眉睫!
  19. Python爬虫:史上最详细的Python爬虫库urllib讲解,绝对经典,值得收藏
  20. 网上书城项目分析及前端页面

热门文章

  1. 如何从Fiori launchpad发出的请求判断出后台是哪个网关系统在响应
  2. Cloud for Customer里权限控制Business Role和Access context的截图
  3. 如何找到odata服务实现的具体backend 系统
  4. 使用postman删除Marketing Cloud里的contact数据
  5. How to bind multiple properties with formatter on one control
  6. C4C和Marketing Cloud的lead以及activity的replication
  7. Product Overview page data loss handling
  8. 我给非洲医药基金会捐的款和感谢信
  9. SAP应用搜索分页的实现原理
  10. 从程序猿到SAP产品经理,我是如何转型的?