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

這樣一來,就實現了一層嵌套,增長了一個節點。提及來有點繞。但請必定要理解這個引用的思想。

相關文章
相關標籤/搜索