链表—两数相加

  • Post author:
  • Post category:其他




2.链表—两数相加 python

难度 中等



题目

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

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

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



示例1

输入:l1 = [2,4,3], l2 = [5,6,4]

输出:[7,0,8]

解释:342 + 465 = 807



示例2

输入:l1 = [0], l2 = [0]

输出:[0]



示例3

输入:l1 = [9,9,9,9,9,9,9], l2 = [9,9,9,9]

输出:[8,9,9,9,0,0,0,1]




解题思路


此题要考虑两点问题


  • 链表不等长问题

    :对于链表不等长问题,解法不同,考虑的也不同。若将结果保存到新链表中,则链表长度不同,只需考虑求和值的问题。若将结果覆盖到原链表中的某一链表中,不仅考虑和值问题,还要考虑若被覆盖值的链表先为空时,被覆盖值的链表应链接到另一链表的问题

  • 进位问题

    :对于进位问题,需注意,若当前节点相加有进位,则此进位应该被加入后一节点(进位向后扔)。故在算法中首先要初始化一个进位值为0(首节点没有进位值被加入),而且求进位应该在算出当前节点结果值之后,这样才能保证所求进位被加入的是下一节点,而不是当前节点。对于最后节点也要考虑到,若有进位,则应该在结果最后构造一个新节点(节点值为1,因为进位为1)。



解法1:构造新的链表用于保存结果

  1. 初始化一个新链表的头结点 r=ListNode(),节点值默认为0
  2. 定义一个指针cur指向新链表的头结点(cur不动,此题结果保存在r头结点的下一节点r.next中,则返回cur.next即为返回结果)
  3. 初始化进位值carry为0
  4. 链表不等长分情况讨论如下:
  • 链表l1为空时,和值为l2的值加上进位
  • 链表l2为空时,和值为l1的值加上进位
  • l1,l2都不为空时,和值为l1+l2+carry
  1. 求新链表中保存的结果:应为和值对10取余,即余数remainder=和值%10
  2. 求完结果,则两个链表的指针l1,l2向后移,即l1=l1.next ,l2=l2. next
  3. 将结果保存到新建链表的头结点的下一节点中,然后新链表的指针r向后移
  4. 保存完结果求进位,进位为和值对10向下取整,即carry=和值//10
  5. 第4到第8为一个循环体,控制条件为l1或l2都不为空,当退出循环时,则l1,l2都为空,说明已经计算到两个链表的最后节点,此时退出循环,考虑最后节点是否有进位,若carry=1,则最后节点和值有进位,应向后一节点进1,则r.next=ListNode(1)
  6. 最后返回cur.next即为返回结果



代码

 class ListNode(object):
    def __init__(self, val=0, next=None):
         self.val = val
        self.next = next
 class Solution(object):
    def addTwoNumbers(self, l1, l2):
        """
        :type l1: ListNode
        :type l2: ListNode
        :rtype: ListNode
        """
        r = ListNode()  # 创建链表的头结点,节点值默认为0
        cur = r  # cur指向头结点,头结点值默认为0,则返回为头结点的next,next为结果链表的头结点
        carry = 0  # 初始进位为0
        while l1 or l2:
        	if l1 is None:
        		s = l2.val + carry
        	if l2 is None:
        		s = l1.val + carry
        	if l1 and l2;
        		s = l1.val + l2.val + carry
        	# 求余数
        	remainder = s % 10
        	# 取完两个链表值,求完结果,则两链表指针向下走
        	if l1 is not None:
        		l1 = l1.next
        	if l2 is not None:
        		l2 = l2.next
        	# 将结果链接到头结点的下一个节点
        	r.next = ListNode(remainder)
        	# 存完结果,指针向下走
        	r = r.next
        	# 求进位
        	carry = s // 10
        # 退出循环,则l1,l2都到了最后节点的下一个空节点,还要考虑最后节点相加有进位的情况
        if carry == 1:
        	r.next = ListNode(1)
        return cur.next



解法2:将结果覆盖到某一链表中(此处覆盖到链表l1中)

  1. 初始化进位值carry为0
  2. 定义一个指针cur指向链表l1的头结点(和解法1一样,cur不动,最后返回cur即为返回结果)
  3. 链表长度不同,分情况讨论如下
  • 当l1下一节点为空时,此时当前节点的和值仍为l1+l2+carry,其余数remainder=和值%10,则修改l1的值为其余数,进位carry=和值//10
  • 改完当前节点l1的值后,l1的下一节点应该链接到l2的下一节点上,即l1.next=l2.next
  • 此处为循环,当l1.next不为空时,l1.next.val=(l1.next.val+carry)%10,carry=(l1.next.val+carry)//10,此处注意,要将l1.next.val先用一个变量存储,防止计算carry时,l1.next.val改变,然后l1指针向后移
  • 退出循环,则l1.next为空,当carry为1时,同理要加一个新的值为1的节点,即l1.next=ListNode(1)
  • 最后返回cur即返回结果

  • 当l2下一节点为空时,同样,当前节点和值还是l1.val+l2.val+carry,余数remainder=和值%10,则修改l1的值为其余数,carry=和值//10
  • 此处为循环,当l1.next不为空时,l1.next.val=(l1.next.val+carry)%10,carry=(l1.next.val+carry)//10,同样要注意此处l1.next.val用一个变量存储
  • 退出循环,则l1.next为空,若carry=1,则要加一个新的值为1的节点,即l1.next=ListNode(1)
  • 最后返回cur即为返回结果

  • 当l1和l2下一节点都不为空时,其和值为s=l1.val+l2.val+carry,余数remainder=和值%10,l1.val=remainder,指针l1,l2向下走,进位carry=和值//10



代码

carry = 0
cur = l1  # 定义指针cur指向链表l1的头结点
while l1 or l2:
	if l1.next is None:
		y1 = l1.val + l2.val + carry  # 当前节点的和值
		l1.val = y1 % 10  # 修改当前节点的值
		carry = y1 // 10  # 进位
		l1.next = l2.next  # 将l1的下一个节点指向l2的下一个节点
		while l1.next:
			y = l1.next.val + carry  # 下一节点的和值
			l1.next.val = y % 10  # 修改下一节点的值
			carry = y // 10  # 进位
			l1 = l1.next  # 指针l1后移
		# 退出循环,若有进位,则再加一个值为1的节点
		if carry == 1:
			l1.next = ListNode(1)
		# 返回结果
		return cur
	if l2.next is None:
		x1 = l1.val + l2.val + carry
		l1.val = x1 % 10
		carry = x1 // 10
		while l1.next:
			x = l1.next.val + carry
			l1.next.val = x % 10
			carry = x // 10
			l1 = l1.next
		if carry == 1:
			l1.next = ListNode(1)
		return cur
	if l1.next and l2.next:
		s = l1.val + l2.val +carry
		l1.val = s % 10
		l1 = l1.next
		l2 = l2.next
		carry = s //10

至此,此题已解答完毕



总结

  • 遇到非单个链表问题,首先就要考虑到链表长度问题,对于不同长度时,要考虑多种情况。
  • 还有链表指针的问题,有些链表类的题要尤其注意指针移动的问题,如果你想知道,请关注我之后的文章o( ❛ᴗ❛ )o
  • 此题要注意,在代码开始就要定义一个指针指向你所要输出结果的链表的头结点,最后返回这个指针,即为返回结果
  • 此题还可以用递归来写,到后面递归问题我再着重写递归思想



版权声明:本文为qq_48158780原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。