小白的LeetCode刷题日记2--python3

LeetCode第2题:两数相加

题目描述:

来自LeetCode

要求:

来自LeetCode

第一种解法:遍历逐位相加

这题相比第一题,采用了单链表这个数据结构来进行。在解该题时,可以先把我们的思维带入,两数相加在我们实际生活中是如何计算的,两数从低位开始对齐,然后逐渐相加,而题目中正好是逆序存放,这就刚刚好符合我们计算时的模式,因此我们只需要新建一个单链表,然后依次遍历所给的链表L1和L2的所有结点,并将其对应的val值相加赋给我们新建的单链表结点即可

result = ListNode() #初始链表

我新建了一个result链表,根据下图的定义便可以知道这个新建的result的val和next会分别赋予0和None
来自LeetCode
在我们实际计算的时候,还会碰到进位的情况,因此还需要设置一个进位变量以便后续的计算进行

carry = 0 #进位标志

设置了初始链表和进位标志后,因为后续是需要移动链表的,而单链表的性质迫使指针next无法指向上一个结点,因此每当移动一次后,前面的数据就无法查找,因此我们需要再定义一个链表,令result指向它,这时result就起到了头指针的作用,让我们在后面可以返回正确的链表

tmp = result

做完了初始工作,就开始解题了,首先我们要判断所给的L1和L2哪个链表长度短,因为next指针指向的是None,不一致的话超过部分无法直接相加,因此先判断哪个链表短,这时只需要用一个while循环条件判断即可,代码如下:

while l1 and l2:

因为while的条件判断是True时执行,and运算会使当其中一个链表为空时变为False,这时就会停止while循环判断

接下来就是对链表进行赋值了,首先先计算L1和L2当前的val并和carry(进位)值相加,这样就得到了最低位的相加结果

sum = l1.val + l2.val + carry

接下来就对tmp进行赋值,我们让tmp的下一个指针指向的val值等于sum模10,这样就取得了第一个数,模10的原因是因为当相加超过10时可以去到个位数的值,如相加得12,那么个位数就是2,符合我们正常的计算,此时,进位就是1,那么进位的表达式就是sum整除10

tmp.next = ListNode(sum % 10)
carry = sum // 10

然后就是移动指针方向,使其指向下一个结点

l1 = l1.next
l2 = l2.next
tmp = tmp.next

这样while循环就结束了,但是while循环的作用是讲两个链表从第一个开始相加,加至等长部分,那么当链表不等长时,就需要额外再进行相加,所以用if判断语句来进行,赋值方式同while循环那块代码类似

if l1:
    while l1 :
        sum = l1.val + carry
        tmp.next = ListNode(sum % 10)
        carry = sum // 10
        l1 = l1.next
        tmp = tmp.next 
if l2:
  while l2 :
      sum = l2.val + carry
      tmp.next = ListNode(sum % 10)
      carry = sum // 10
      l2 = l2.next
      tmp = tmp.next 

那么为什么要进行这么长的赋值而不直接讲tmp的指针直接指向L1(或L2)的剩余部分链下去呢?这是因为存在进位情况,万一出现进位,那么L1(或L2)的剩余部分的val值将会产生改变,直接链下去就会使结果产生错误

再接下来,因为我们之前计算的结束条件是L1和L2都到空的情况,但是如果最后一位还产生进位情况时,就无法得到正确结果,因此还需要对进位进行判断,若carry为1时则再新建一个结点并赋值为1

if carry == 1:
    tmp.next = ListNode(1) 

最后,我们返回result的下一个结点即可,因为第一个结点是0,充当的是头结点的作用,无需返回第一个结点

return result.next

完整代码如下:

class Solution:
    def addTwoNumbers(self, l1: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:
        carry =0
        result = ListNode()
        tmp = result
        while l1 and l2 :
            sum = l1.val + l2.val + carry
            tmp.next = ListNode(sum % 10)
            carry = sum // 10
            l1 = l1.next
            l2 = l2.next
            tmp = tmp.next  
        if l1:
            while l1 :
                sum = l1.val + carry
                tmp.next = ListNode(sum % 10)
                carry = sum // 10
                l1 = l1.next
                tmp = tmp.next 
        if l2:
            while l2 :
                sum = l2.val + carry
                tmp.next = ListNode(sum % 10)
                carry = sum // 10
                l2 = l2.next
                tmp = tmp.next 
        if carry == 1:
            tmp.next = ListNode(1)    

        return result.next
复杂度分析

时间复杂度:因为我们先是遍历的是所给的链表L1和L2的等长部分,再遍历剩余部分,因此实际上就是遍历了L1和L2当中长度最长的那个,设L1长度为m,L2长度为n,则时间复杂度为O(max(m,n)),即取长度最长的链表
空间复杂度:空间复杂度取决于我们新建的链表result的长度,最长就是max(m,n)+1,即最后一位还有进位的情况,那这个+1其实无关痛痒,可以省略,因此空间复杂度为O(max(m,n))

优缺

运行结果
暂时还不知道优缺点,有知道的可以评论告诉我下(doge)

第二种解法:递归

解法参考如图所示
该解法参考LeetCode第二题讨论区的名叫:FractalPhi的用户
首先是判断L1和L2是否非空,若L1空,则返回L2,若L2空,则返回L1,这里因为第一种只需要调用一次addTwoNumbers函数即可得到结果,而且题目给的是非空链表,因此第一种解法不需要判断空还是不空,但是递归解法是链表的递归后一直调用addTwoNumers函数,会有链表为空的情况产生,因此需要判断

if not l1:
	return l2
if not l2:
	return l1

接下去就进行对应位加法操作

sum = l1.val + l2.val

紧接着就开始进行递归,返回值是结点

if sum > 9:
    return ListNode(sum-10, self.addTwoNumbers(ListNode(1, None), self.addTwoNumbers(l1.next, l2.next)))
else:
    return ListNode(sum, self.addTwoNumbers(l1.next, l2.next))

我们以输入L1=[2,4,3],L2=[5,6,4]为例来进行讲解
1、首先先判断最低位相加,2+5=7,7<9,进行else部分,此时返回的是一个结点,值为7,next指针指向的是调用addTwoNumbers函数后的某个未知结点①,这个结点①是经过又调用addTwoNumbers函数得来的,我们来看接下去的运行如何确定结点①

2、接下去进行上一步中的递归self.addTwoNumbers(l1.next,l2,next),此时传入的是L1和L2的next,此时运行函数addTwoNumbers,现在sum的值为4+6=10,10>9,进行的是if sum > 9:的部分,此时返回的结点值是sum-10=0,指针指向的是某个未知结点②,这个结点②是也是调用函数相加得来,但是在传入的两个链表参数中,第二个链表需要递归后才能得到,按照执行顺序,我们需再执行addTwoNumbers函数才能确定结点②

3、这时候进行的是确定结点②,依照上一步,我们先计算addTwoNumbers函数返回的结点,此时sum=3+4=7,7<9,执行的是else部分,此时返回了一个未知结点③,③是通过函数计算得来,此时再进行函数计算,因为现在L1和L2的元素都已经为空,根据判断条件,所以返回的是L2,L2是一个空的链表,因此未知结点③的val值为7,next指针指向None

4、确定了未知结点③,此时就开始确定未知结点②,②是由ListNode(1,None)和结点③(7,None)调用函数相机得到的,所以结点②的val值为8,指针指向空

5、结点②确定了,就能够确定结点①,结点①是由ListNode(0,结点②)得来的,因此结点①的val值为1,next指针指向的是结点②

6、到此就结束了递归之旅,因此可以确定,此时的链表关系是(7,结点①),结点①(0,结点②),结点②(8,空),正好是正确答案

过程如图(省略计算)在这里插入图片描述

复杂度分析

时间复杂度:递归的深度就是两个链表最长的长度,即O(max(m,n)
空间复杂度:还是建立的链表长度,即O(max(m,n)

优缺

在这里插入图片描述还是不懂,唯一知道的是递归的缺点就是可能会出现内存溢出,因为在递归调用的过程中都会在内存栈中分配空间,这当长度很长时可能会出现溢出,while循环则只是调用一次addTwoNumbers函数,每次循环后都会结束并且释放内存,并不会一直在内存栈中请求空间,所以无此忧虑

以上请大家多批评指正!
图均参考LeetCode

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值