1. 程式人生 > 其它 >Leetcode的第2題,持續改進演算法到cleancode

Leetcode的第2題,持續改進演算法到cleancode

技術標籤:2020演算法python

這是非常典型的,同樣是編程式碼完成一樣的功能,基礎也夠,但是寫出來的速度、容易度、可閱讀性、簡潔性差異極大。這得看天賦和經驗,絕不是簡單搬磚。這是歷來強調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處理就要反覆考慮進位、連結串列延長等問題