. 题目
. 准备知识
第一眼看到这个题目,小明真的懵逼了。不怕大家笑话,小明不是CS科班出身,没有尝过数据结构和算法这些个基础课程。关于我的个人背景,在「每周思考|学习什么样的知识才能获益终生?」里有提到,感兴趣的同学,可以看一下。
但是小明并不怕,现学现用,一直是小明很喜欢干的事。不能总是准备好了再出发,等你花尽心机用了几个月时间去学完了数据结构和算法,却早已精疲力尽,忘记了初衷。
为了完成这道题,我立马上网,搜寻了数据链表
的相关知识。
网上的理论内容很多,要深入理解的可以使用搜索引擎捞一下。
在这里不会讲那么细。
一是、讲得太细,要讲好多,篇幅必将拉得很长。
二是、讲得太多,你还不一定能懂得这是啥。
所以,我也在想到底怎么样才能用一句话或者一张图就把这件事说清楚。
一句话:链表
,用拆词法来看,是由一条链(链由多个节点连接组成)来表示一个列表的对象。
每个节点,我们称之为 Node
,该Node
有两个属性,一个是val,存放当前节点的值,一个是next,存放下一个节点的地址。
一张图
如下图,就是嵌套存储下节点的值,每个当前节点的next,都是下一节点对象。
. 我的版本
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
head = _head = ListNode(0)
# 是否进位,要嘛为0,要嘛为1
flag = 0
while l1 or l2:
v1 = v2 = None
if l1:
v1 = l1.val # 取出当前节点值
l1 = l1.next # 并重新赋值,为下个循环做准备
if l2:
v2 = l2.val # 取出当前节点值
l2 = l2.next # 并重新赋值,为下个循环做准备
'''
因为后面v1和v2后面被del了
所以这边要么有值要么为0
'''
v1 = v1 or 0
v2 = v2 or 0
flag, value = divmod(v1+v2+flag, 10)
# 增加节点
_head.next = _head = ListNode(value)
# 这句是对特殊情况进行处理,下面会讲
if not l1 and not l2 and flag == 1:
_head.next = _head = ListNode(1)
del v1,v2
return head.next
运行一下,还算理想。击败了93.66%
,今天又可以加个鸡腿了。
在上面我的代码中有这么一段,是做什么的。
if not l1 and not l2 and flag == 1:
_head.next = _head = ListNode(1)
我的第一遍代码是没有这段的,测试数据也通过,但在提交答案的时候,在大量测试用例下,有一个场景没有考虑到,就是两数的最高位,相加进一的时候就会出错。比如下面这种。
. 网上的版本
按照惯例,还是上网去看看别人的优秀代码。结果,真的让小明大吃一惊,和我一样的逻辑,但是代码可对我精练多了。大家可以对比学习一下。
class Solution(object):
def addTwoNumbers(self, l1, l2):
"""
:type l1: ListNode
:type l2: ListNode
:rtype: ListNode
"""
head = p = ListNode(0)
carry = 0
while l1 or l2 or carry:
if l1:
carry += l1.val
l1 = l1.next
if l2:
carry += l2.val
l2 = l2.next
carry, val = divmod(carry, 10)
p.next = p = ListNode(val)
return head.next
. 难点梳理
在以上代码中,有一个新手可能难以理解的是,下面这个用法。
class Node:
def __init__(self, val):
self.val = val
self.next = None
header = n = Node(2)
n.next = n = Node(4)
通常来说,Python 中的 =
很多人可能会理解为 赋值
,在大多数情况,赋值确实很通俗易懂,但是在如上这种情况下,如果你再用赋值
去理解,你可以发现,怎么都解释不通。
所以这里,小明认为,=
准确的理解 应该是 引用
。
第一句 header = n = Node(2)
Node(2)
首先在内存中取得一席之地(内存地址),存放其值。
然后,创建一个变量名为header
的对象,并将其指向Node(2)
的地址。
最后,再创建一个变量名为n
的对象,也将其指向Node(2)
的地址。
这样,header和n就都是Node(2)的代言人,对header和n中的任一变量做改变,另一变量也将随之变化,因为他们两个本就是一个对象。和下面代码这种是一样的,你一定知道其中原理。
>>> a = b = [1,2,3]
>>> a
[1, 2, 3]
>>> b
[1, 2, 3]
>>> # 对b添加元素
>>> b.append(6)
>>> b
[1, 2, 3, 6]
>>> a # 发现a也随之改变
[1, 2, 3, 6]
第二句 n.next = n = Node(4)
Node(4)
首先在内存中取得一席之地(内存地址),存放其值。
然后,将之前的变量n
的next属性,指向Node(4)
的地址。本质上是改变了Node(2)
的next 指向的是Node(4)
最后,将变量n重新指定Node(4)
,这时候,n
就相当于header.next
。
这样一来,就实现了一层嵌套,增加了一个节点。说起来有点绕。但请一定要理解这个引用
的思想。