Leetcode的第2題,持續改進演算法到cleancode
阿新 • • 發佈:2021-01-12
這是非常典型的,同樣是編程式碼完成一樣的功能,基礎也夠,但是寫出來的速度、容易度、可閱讀性、簡潔性差異極大。這得看天賦和經驗,絕不是簡單搬磚。這是歷來強調clean code和程式碼質量的一個原因。
方法上教訓自己的就是多看、多讀、編碼前多構思,不要憑著衝勁兒蠻幹。
titile
給出兩個 非空 的連結串列用來表示兩個非負的整數。其中,它們各自的位數是按照 逆序 的方式儲存的,並且它們的每個節點只能儲存 一位 數字。
如果,我們將這兩個數相加起來,則會返回一個新的連結串列來表示它們的和。
您可以假設除了數字 0 之外,這兩個數都不會以 0 開頭。
示例:
輸入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
輸出:7 -> 0 -> 8
原因:342 + 465 = 807
第1版,直接按照加法累加,搬移2個listnode到第三個,58line
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
def judgeSym(x, bit, y = 0):
#判斷三個數和是否大於等於10,返回值和進位符
tmp = x + y + bit
if tmp >= 10 :
return tmp-10, 1
else:
return tmp, 0
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
rst = []
bit = 0
while (l1.next and l2.next):
x,bit = judgeSym(l1.val, bit, l2. val)
rst.append(x)
l1 = l1.next
l2 = l2.next
#最後一個值別忘記累加,沒有next但是有當前的val
x, bit = judgeSym(l1.val, bit, l2.val)
rst.append(x)
l1 = l1.next
l2 = l2.next
if l1:
while(l1.next):
x, bit = judgeSym(l1.val, bit)
rst.append(x)
l1 = l1.next
x,bit = judgeSym(l1.val, bit)
rst.append(x)
if l2:
while(l2.next):
x, bit = judgeSym(l2.val, bit)
rst.append(x)
l2 = l2.next
x,bit = judgeSym(l2.val, bit)
rst.append(x)
if bit > 0:
rst.append(bit)
h = ListNode(rst[0])
y = h
for it in rst[1:]:
y.next = ListNode(it)
y = y.next
return h
第2版,檢視長度,累加到長的連結串列上,避免多個while去盲做,63line
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
"""
參考leetcode不錯的簡潔思路:契合連結串列這種演算法結構,且有效利用了輸入的節點;
影響是改變了輸入的節點值;
1)計算兩個listnode,如果l1比l2短,交換他們,確保l1總是小於l2;
2)這樣同時遍歷l1、l2(l2短,以它截止為準),值加到l1上,注意進位處理
:param l1:
:param l2:
:return:
"""
if self.getLen(l2) > self.getLen(l1):
l1, l2 = l2, l1
head = l1
tail = 0
while l2:
l1.val = l1.val + l2.val + tail
if l1.val >= 10:
l1.val -= 10
tail = 1
else:
tail = 0
if l1.next:
l1 = l1.next
elif tail:
l1.next = ListNode(1)
l1 = l1.next
tail = 0
l2 = l2.next
#l1的進位沒有處理完,防止出現9999 增加1之後全部進位,尾部可能新增1個
while tail:
l1.val += tail
if l1.val >= 10:
l1.val -= 10
tail = 1
else:
tail = 0
if l1.next:
l1 = l1.next
elif tail:
l1.next = ListNode(1)
tail = 0
l1 = l1.next
return head
def getLen(self, l1:ListNode)->int:
length = 0
while(l1):
length += 1
l1 = l1.next
return length
第3版,判決長度,進位分離開單獨做,49line
# Definition for singly-linked list.
# class ListNode:
# def __init__(self, val=0, next=None):
# self.val = val
# self.next = next
class Solution:
def addTwoNumbers(self, l1: ListNode, l2: ListNode) -> ListNode:
"""
參考leetcode不錯的簡潔思路:契合連結串列這種演算法結構,且有效利用了輸入的節點;
影響是改變了輸入的節點值;
1)計算兩個listnode,如果l1比l2短,交換他們,確保l1總是小於l2;
2)這樣同時遍歷l1、l2(l2短,以它截止為準),值加到l1上,注意進位處理
特別注意,進位的處理分離出來會非常有利,少判斷很多條件,解耦listnode的next方法;很clean code
:param l1:
:param l2:
:return:
"""
if self.getLen(l2) > self.getLen(l1):
l1, l2 = l2, l1
head = l1
while l2:
l1.val = l1.val + l2.val
l1 = l1.next
l2 = l2.next
#l1的進位沒有處理完,防止出現9999 增加1之後全部進位,尾部可能新增1個
p = head
while p:
if p.val >=10:
p.val -= 10
#自然而然的進位,下一個存在就next並值遞增,否則到了尾巴,新建一個listnode賦值1,並連綴到l1尾巴上
if p.next:
p = p.next
p.val += 1
else:
p.next = ListNode(1)
else:
p = p.next
return head
def getLen(self, l1:ListNode)->int:
length = 0
while(l1):
length += 1
l1 = l1.next
return length
commit
- 功能都ok,但是完成速度和燒腦性逐個變快、變簡;可讀性變好
- 兩個關鍵:2個數、多個結構體,長度、型別等屬性不以操作,首先判決後找最大的那個累積操作,而不要一直做下去,結束做判斷
- 特殊條件的特別操作,可以集中處理,免於疊加處理的開銷和增加複雜。如這裡的進位,分離處理快速、方便、邏輯也比較嚴謹。而第二版,融合進a+b處理就要反覆考慮進位、連結串列延長等問題