python力扣算法笔记(两数相加)

一、题目描述

给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。

请你将两个数相加,并以相同形式返回一个表示和的链表。

你可以假设除了数字 0 之外,这两个数都不会以 0 开头。

输入:l1 = [2,4,3], l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807.
输入:l1 = [0], l2 = [0]
输出:[0]
输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]
输出:[8,9,9,9,0,0,0,1]

二、解题思路+代码

思路:链表是一种基础的数据结构。而python没有专门的指针概念,在python中每一个变量都可以是指针。所以这道题需要额外定义链表类的代码:

class ListNode(object):
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

注:这是题目原先就提供的注释代码,可以解开注释,也可以不解开,都可以提交得到结果。只不过注释解开之后执行用时会更长一些。

2.1 方法一:列表法(直接处理)

列表是python中处理数据的常用结构。根据惯性思维,可以先将链表结构转化为python中熟悉的列表结构,然后对列表元素进行处理。最终,将结果再转换为链表输出。

代码:

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        # 链表转成列表
        a1=[]
        a2=[]
        while(l1):
            a1.append(l1.val)
            l1=l1.next
        while(l2):
            a2.append(l2.val)
            l2=l2.next

        result = []  # 定义空列表用于存储结果
        carry = 0  # 初始化进位为0
        for i in range(0, max(len(a1), len(a2))):  # 遍历两个列表中的最大长度
            if i < min(len(a1), len(a2)):
                sum = a1[i] + a2[i] + carry
                carry = sum // 10
                num = sum % 10
                if len(a1) == len(a2) and i == len(a1) - 1 and carry == 1: # 如果两个列表长度相等,且最后一位相加需要进位
                    result.append(num)
                    result.append(carry)
                else:
                    result.append(num)
            else:  # 如果两个列表有位数之差
                l_max = max(a1, a2, key=len)
                sum = l_max[i] + carry
                carry = sum // 10
                num = sum % 10
                if i == len(l_max) - 1 and carry == 1:  # 如果最后一位相加需要进位
                    result.append(num)
                    result.append(carry)
                else:
                    result.append(num)
        
        # 新建两个空链表
        node = ListNode(0)
        tmp_node = node 
        # 遍历结果列表,插入链表
        for x in result:
            tmp_node.next = ListNode(x)
            tmp_node = tmp_node.next
                      
        return node.next

上面代码中,将结果列表转化为链表的过程可能会有小伙伴不太能理解。下面讲讲我自己的理解:

(1)首先node=ListNode(0)定义了一个头指针,代表链表的位置,可以通俗地理解为现在的node值为0,而指针就指着这个0。接着把node赋值给tmp_node,现在两个变量的地址相同了,可以理解为在tmp_node上的操作也会改变node链表(根据ListNode类的定义,现在tmp_node.val=0, tmp_node.next=None)。

(2)然后就是遍历结果列表(假设结果列表为[8, 1, 7])。

① 首先遍历得到第一个元素8,将这个元素定义为ListNode类型并赋值给原本为None的tmp_node.next,现在node和tmp_node均为:

0 -> 8

注:两个链表现在的指针均指向0,且node.next和tmp_node.next均为8。

接着,tmp_node=tmp_node.next将指针向后移了一位,现在的tmp_node=8,即tmp_node的指针指向8,且赋值后的tmp_node.next=None。

② 接着遍历得到第二个元素1,将这个元素定义为ListNode类型并赋值给原本为None的tmp_node.next,由于在tmp_node上的操作也会改变node链表,所以现在node为:

0 -> 8 -> 1

注:现在两个链表的指针的指向就不同了。node链表依然指向0,且node.next=8->1。而tmp_node链表指向8,且tmp_node.next=1

接着,tmp_node=tmp_node.next将指针向后移了一位,现在的tmp_node=1,即tmp_node的指针指向1,且赋值后的tmp_node.next=None。

③ 最后遍历得到第三个元素7,将这个元素定义为ListNode类型并赋值给原本为None的tmp_node.next,由于在tmp_node上的操作也会改变node链表,所以现在node为:

0 -> 8 -> 1 -> 7

注:两个链表的指针指向依然不同。node链表依然指向0,且node.next=8->1->7。而tmp_node链表指向1,且tmp_node.next=7

接着,tmp_node=tmp_node.next将指针向后移了一位,现在的tmp_node=7,即tmp_node的指针指向7,且赋值后的tmp_node.next=None。

到这里,列表所有元素就遍历完了,最终得到的node.next=8->1->7就是我们想要的结果链表。因此返回的是node.next。在上述的遍历过程中,可以发现最初定义的头指针node始终指向链表的头部,头指针不能移动,头指针如果移动了,那么头指针之前的数据就会丢失。而遍历中一直在移动的是tmp_node的自动指针,用于指向需要操作的数据。

上述解释纯属个人理解,如果有出错的地方,请多多包涵,并恳请指正,感谢万分!

2.2 方法二:列表法(字符处理)

上述方法是直接在列表元素上进行操作,但python中可以直接将数字形式的字符串转化为int类型,利用这一特性,可以取个巧。

代码:

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        # 链表转成列表
        a1 = []
        a2 = []
        while (l1):
            a1.append(l1.val)
            l1 = l1.next
        while (l2):
            a2.append(l2.val)
            l2 = l2.next

        # 列表转成字符串
        s1 = ''
        for i in range(len(a1) - 1, -1, -1):
            s1 = s1 + str(a1[i]) 
        s2 = ''
        for j in range(len(a2) - 1, -1, -1):
            s2 = s2 + str(a2[j])
        # 字符串转成整数
        m1 = int(s1)
        m2 = int(s2)
        # 整数之和转成字符串
        m3 = str(m1 + m2)

        # 新建两个空链表
        tmp_node = ListNode(None)
        node = ListNode(None)
        # 从后往前遍历字符串,并生成链表
        for x in m3[::-1]:
            if not tmp_node.val:
                tmp_node.val = x
                node = tmp_node
            else:
                tmp_node.next = ListNode(x)
                tmp_node = tmp_node.next

        return node

注:定义空链表时,tmp_node = ListNode(None)的执行用时要比tmp_node = ListNode(0)更少。

2.3 方法三:迭代法

归根到底,这道题主要是让我们更加了解链表的思想,上述通过列表结构间接完成的方法略微有一些本末倒置。通过上述方法一的分析,对链表不太熟悉的小伙伴应该已经对链表的机制有了一定的了解了,所以下面的方法将通过链表操作完成。

首先是迭代法,迭代法通过迭代链表的每一个值,并做相加和移位操作。

代码:

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        if l1 is None:
            return l2
        elif l2 is None:
            return l1
        
        # 一个头指针,一个自动指针,头指针不能动,代表链表的位置,一旦移动,头指针之前的数据,就会丢失
        dummy = ListNode(0)
        p = dummy
        carry = 0

        while l1 and l2:
            p.next = ListNode((l1.val + l2.val + carry) % 10)
            carry = (l1.val + l2.val + carry) // 10
            l1 = l1.next
            l2 = l2.next
            p = p.next

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

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

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

        return dummy.next
2.4 方法四:递归法

除此之外,还可以使用递归法实现。解题思路如下图所示:

由上图,首先计算初始值的两数之和,得到进位值。如果指针下一位有值,就移动指针,得到新的链表;如果指针下一位没有值,就补0。然后将得到的进位值和其中一个链表值相加得到新的链表。最后再进行递归。

代码:

class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        total = l1.val + l2.val
        carry = total // 10  # 计算初始化进位
        result = ListNode(total % 10)  # 初始化结果链表

        if l1.next is not None or l2.next is not None or carry != 0:
            if l1.next is not None:
                l1 = l1.next  # 指针后移一位,得到新的l1
            else:
                l1 = ListNode(0)  # 补0

            if l2.next is not None:
                l2 = l2.next  # 指针后移一位,得到新的l2
            else:
                l2 = ListNode(0)  # 补0

            l1.val = l1.val + carry  # l1和进位相加后得到新的链表(此处换成l2也是可以的)
            result.next = self.addTwoNumbers(l1, l2)  # 对新的l1和l2递归调用两数相加函数

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值