欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Leetcode的第2题,持续改进算法到cleancode

程序员文章站 2024-03-21 19:54:22
...
这是非常典型的,同样是编代码完成一样的功能,基础也够,但是写出来的速度、容易度、可阅读性、简洁性差异极大。这得看天赋和经验,绝不是简单搬砖。这是历来强调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处理就要反复考虑进位、链表延长等问题