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处理就要反复考虑进位、链表延长等问题