一、题目描述
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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