LeetCode hot100:002 两数相加(链表):逆序存储数字的加法运算
问题描述:
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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]
解决方法:
方法一:递归法
算法思路:
递归法采用分治思想,将大问题分解为相同结构的子问题,通过函数调用栈自然处理进位传递。
代码实现:
def addTwoNumbers(l1, l2, carry=0):# 递归终止条件:两个链表都为空且无进位if not l1 and not l2 and carry == 0:return None# 计算当前位的值val1 = l1.val if l1 else 0val2 = l2.val if l2 else 0total = val1 + val2 + carry# 创建当前节点current = ListNode(total % 10)# 递归处理下一位l1_next = l1.next if l1 else Nonel2_next = l2.next if l2 else Nonecurrent.next = addTwoNumbers(l1_next, l2_next, total // 10)return current复杂度分析:
- 时间复杂度:O(max(m,n))
- 空间复杂度:O(max(m,n))(递归栈空间)
方法二:迭代法(推荐)
算法思路:
迭代法采用逐位相加、实时进位的策略,通过循环遍历两个链表,模拟手工竖式加法的过程。
算法步骤:
创建一个虚拟头节点,用于简化链表操作
遍历两个链表,逐位相加
处理进位
如果遍历完后还有进位,需要额外创建一个节点
返回结果链表的头节点
代码实现:
# 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: Optional[ListNode], l2: Optional[ListNode]) -> Optional[ListNode]:result = ListNode(0)newhead = resultcarry = 0while l1 or l2 or carry:# 获取当前位的值,如果链表已经遍历完则值为0l1_val = l1.val if l1 else 0l2_val = l2.val if l2 else 0# 计算当前位的和total = l1_val + l2_val + carry# 计算当前位的进位carry = total // 10current_num = total % 10# 创建新节点newhead.next = ListNode(current_num)newhead = newhead.next# 移动到下一个节点if l1:l1 = l1.nextif l2:l2 = l2.nextreturn result.next
复杂度分析:
- 时间复杂度:O(max(m,n))
- 空间复杂度:O(1)(返回值不计入空间复杂度)
问题详解:
数字存储方式:
链表头存储数字的个位数
后续节点依次存储十位、百位、千位...
例如:数字 342 存储为 2 → 4 → 3
该存储方式符合从个位依次相加的思路
关键点:
逐位相加:从最低位(链表头)开始计算
进位处理:和超过10时需要向高位进位
链表长度不同:两个数字的位数可能不同,不足为0
最高位进位:最后可能产生额外的进位,如示例3
移动到下一个节点:
因为循环条件是 while l1 or l2 or carry ,使用的是 or 不是 and ,需要进行空值判断。这个操作中判断是否为空是必须的,这样可以允许一个链表先结束,处理边界情况
总结:
| 方法 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 递归法 | 代码简洁,思路直观 | 栈空间开销 | 理解递归思想 |
| 迭代法 | 空间效率高,逻辑清晰 | 大多数情况,推荐使用 |
逆序存储简化了加法运算,从个位开始计算更符合数学习惯
进位处理是算法的关键,需要仔细处理各种进位情况
虚拟头节点技巧可以大幅简化链表操作
边界情况需要考虑周全,特别是链表长度不同和最高位进位
