LeetCode 刷题记录 |002:两数相加

题目

image.png

准备知识

第一眼看到这个题目,小明真的懵逼了。不怕你们笑话,小明不是CS科班出身,没有尝过数据结构和算法这些个基础课程。关于个人我的背景,在「每周思考|学习什么样的知识才能获益终生?」里有提到,感兴趣的同窗,能够看一下。算法

可是小明并不怕,现学现用,一直是小明很喜欢干的事。不能老是准备好了再出发,等你花尽心机用了几个月时间去学完了数据结构和算法,却早已精疲力尽,忘记了初衷。数据结构

为了完成这道题,我立立刻网,搜寻了数据链表 的相关知识。app

网上的理论内容不少,要深刻理解的能够使用搜索引擎捞一下。数据结构和算法

在这里不会讲那么细。
一是、讲得太细,要讲好多,篇幅必将拉得很长。
二是、讲得太多,你还不必定能懂得这是啥。ide

因此,我也在想到底怎么样才能用一句话或者一张图就把这件事说清楚。学习

一句话链表,用拆词法来看,是由一条链(链由多个节点链接组成)来表示一个列表的对象。测试

每一个节点,咱们称之为 Node,该Node有两个属性,一个是val,存放当前节点的值,一个是next,存放下一个节点的地址。搜索引擎

一张图
以下图,就是嵌套存储下节点的值,每一个当前节点的next,都是下一节点对象。
spa

个人版本

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%,今天又能够加个鸡腿了。
code

在上面个人代码中有这么一段,是作什么的。

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

这样一来,就实现了一层嵌套,增长了一个节点。提及来有点绕。但请必定要理解这个引用的思想。

相关文章
相关标签/搜索