Medium: Add Two Numbers No.2

Link list by Python

1. link list 数据结构:

数组是由“元素”组成的,字典是由“键值对”组成的,每个数据结构都有它的基本单元。而link list的基本单元就是node:节点。

所以想要弄清楚link list的结构,就要先明白node的结构。

node是一个由两部分组成的unit,一部分是next,指向下一个节点,因此它的值是下一个节点的地址信息,如0x00000241325A3CC8。另一部分是val,就是它本身存储的一个数值信息。

Trick1: 在学习链表的过程中,我一直有一个很困惑的问题,那就是node本身的值。我明白node中存储了两种信息,但是我不知道这两种信息是以怎样的形式被记录以及调用。因此我用print(node)试了一下,发现返回的是一个地址信息,类似0x00000241325A3CC8。因此,node本身的值是一个地址,而想要调用node内部存储的val以及next信息,则需要用 “.” 来访问。

Trick2: 和C语言不同,在python中创建node时不需要手动为node分配存储空间,只需要调用如下函数,就会自动创建一个val为1的节点,地址自动分配,next为None。:

node1 = LinkNode(1)

2. 连续赋值

1) 不可变类型
在看discussion区的范文的时候,有一处代码使用了连续赋值,即:

a = b = 1

这里的a和b之后再更改各自的值,是不会影响对方的。即如果之后执行如下语句,会有这样的对应输出:

.>>> a = b = 1
< a = 2
.>>> print(a)
< 2
.>>> print(b)
<1

这是因为int类型是不可变类型。每次更改int类型变量的值,实际上是重新创建一个不可变类型的对象,并将原来的变量重新指向新创建的对象。在这里,相当于又为 2 创建了一个对象,并让变量 a 指向 2 的位置。而 b 不变,还是指向原来 1 的位置。

除了int类型之外,不可变类型还有string 和 tuple。

2)可变类型
可变类型与不可变类型相反,“可变”指的是创建后的对象可以在原地修改。不同于 int, 如果使用 int 在内存中创建了一个值为 1 的对象,则对应的地址存放的数据永远是 1,不可以更改,想要把变量值改为 2 则需要在内存中新创建一个值为 2 的对象。

而对于可变类型来说,拿 list 举例,list 在 append 一个元素之后, 他的地址仍然指向最初创建时的对象的位置。

因此说回连等,如果是两个 list 连等,那么修改了其中一个 list 的信息,另一个的信息必然也随之改变,因为他们指向同一个地址,索引得到的对象也必然是一样的。

可变对象除了 list 外,还包括 dictionary。

3)Trick:
是在做链表题的过程中想到了这个知识点,因为题目中遇到了两个 node 类的变量连等的问题。因为 node 所构成的结构是链表,下意识的认为链表就是 list 结构,因此对答案百思不得其解。其实任意一个 class 应该都是不可变类型吧,在连等初始化之后,改变其中一个值,对另一个是没有影响的。这道题目中的 class 是这样的,剩下的 class 还没来得及验证,留待之后填坑。

3. 题目

终于能说到题目本身了。
这道题的输入是两个链表,链表中每个节点从小到大代表了加法中的加数在各个分位的值,需要注意的是这里数字的阅读顺序和我们的习惯是反过来的,分位的大小和节点的序号大小是一致的,也就是说要从左往右读数字,最左边的是个位。因此如果有进位 (carry),需要把 carry 加在较大序号的节点运算上,而不是小的。

首先初始化一个 carry 来存放进位,然后初始化两个节点,一个 n 节点,作为存放 sum 结果的链表的首个节点,也就是个位上的数字,同时也由 n 通过 next 串联起整个链表。

其次初始化一个 root 节点,root 节点的初始值应当与 n 相同,因此才能记住 n 的地址,从而索引到结果所在的整个链表。所以在初始化 root 和 n 的时候采用了连等定义的方式。

之后开始进入计算环节,只要两个加数和 carry 中的任意一个不为0,都要把计算进行下去,因此循环终止的条件有三个。

用临时变量存放当下计算出的 sum 和 carry 结果,把 sum 通过 n.next 放入链表中,而 carry 留下参与下一次运算。

在计算当前 sum 和 carry 的过程中用到了一个函数 divmod(Value, Modulo), 这个函数具有取模取余的功能,返回两个参数,第一个返回值是 remainder 也就是余数,第二个是 quotient。 在 divmod() 第一个参数的位置也可以进行运算。

怎样存储整个链表呢?那就是通过 n 和 root 的相互配合。n 可以不断用 n.next 存储整个链表中节点的 value,而 root 则用来记载整个链表的 head 地址。

orz想想这不就是大一wuli lzy老师讲的链表原理吗!!我终于想明白了!!!

4. 代码

写代码之前没有注意到的几点:

  1. 在对链表中的每个节点进行运算的时候,一定要验证每个节点的存在性问题。很简单,通过 if (list) 就可以。在这个条件下进行操作。
  2. 调用任何函数都要注意返回值的顺序问题,就像 divmod() 函数,第一个返回值是 remainder, 第二个才是 quotient。
上一篇:LeetCode 445 两数相加II 题解


下一篇:【稳住,can carry】初识Python字典和集合