两数相加问题

问题描述

给定两个非空链表，表示两个非负整数。每位数字按照逆序方式存储，每个节点存储一位数字。将两个数相加，并以相同形式返回表示和的链表。

假设除了数字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]
```

提示

· 每个链表中的节点数在范围 [1, 100] 内
· 0 <= Node.val <= 9
· 题目数据保证列表表示的数字不含前导零

方法思路

问题分析

两个链表表示的数字是逆序存储的，即链表的头节点表示个位数，第二个节点表示十位数，以此类推。我们需要模拟加法运算，从低位到高位逐位相加，并处理进位问题。

算法选择

· 使用一个虚拟头节点(dummy node)简化链表操作
· 同时遍历两个链表，逐位相加并处理进位
· 如果链表长度不同，较短链表的缺失位视为0
· 最后如果还有进位，需要额外创建一个节点

算法步骤

1. 初始化虚拟头节点和当前指针
2. 初始化进位值为0
3. 同时遍历两个链表，直到两个链表都遍历完且没有进位
4. 计算当前位的和：两个链表当前节点的值加上进位
5. 计算新的进位和当前位的值
6. 创建新节点并连接到结果链表
7. 移动所有指针到下一个位置
8. 返回虚拟头节点的下一个节点

复杂度分析

· 时间复杂度：O(max(m, n))，其中m和n分别是两个链表的长度
· 空间复杂度：O(max(m, n))，结果链表的长度最多为max(m, n) + 1

解决代码

```python
# 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: ListNode, l2: ListNode) -> ListNode:
        """
        将两个逆序存储的数字链表相加，返回结果链表
        
        参数:
        l1: ListNode - 第一个数字链表
        l2: ListNode - 第二个数字链表
        
        返回:
        ListNode - 相加结果的链表
        """
        dummy = ListNode(0)  # 虚拟头节点
        current = dummy      # 当前指针
        carry = 0            # 进位值
        
        # 遍历两个链表，直到两个链表都为空且没有进位
        while l1 or l2 or carry:
            # 获取当前节点的值，如果节点为空则为0
            val1 = l1.val if l1 else 0
            val2 = l2.val if l2 else 0
            
            # 计算当前位的和
            total = val1 + val2 + carry
            carry = total // 10  # 计算进位
            digit = total % 10   # 计算当前位的值
            
            # 创建新节点并连接到结果链表
            current.next = ListNode(digit)
            current = current.next
            
            # 移动到下一个节点
            if l1:
                l1 = l1.next
            if l2:
                l2 = l2.next
        
        return dummy.next  # 返回结果链表的头节点
```

代码解释

1. 链表节点定义：ListNode类定义了链表节点的结构，包含值val和指向下一个节点的指针next。
2. 初始化：
   · 创建虚拟头节点dummy，简化链表操作
   · 设置当前指针current指向虚拟头节点
   · 初始化进位carry为0
3. 循环处理：
   · 使用while循环遍历两个链表，直到两个链表都遍历完且没有进位
   · 循环条件l1 or l2 or carry确保处理所有情况
4. 获取当前值：
   · 如果链表节点存在，获取其值；否则视为0
   · 这样可以处理链表长度不同的情况
5. 计算和与进位：
   · 计算当前位的总和：val1 + val2 + carry
   · 计算新的进位：total // 10
   · 计算当前位的值：total % 10
6. 创建新节点：
   · 根据当前位的值创建新节点
   · 将新节点连接到结果链表
   · 移动当前指针到新节点
7. 移动指针：
   · 如果链表还有节点，移动到下一个节点
   · 这样可以继续处理后续位
8. 返回结果：
   · 返回虚拟头节点的下一个节点，即结果链表的头节点
   · 虚拟头节点简化了链表操作，避免处理空链表的情况

示例验证

以示例1为例：l1 = [2,4,3] (表示342)，l2 = [5,6,4] (表示465)

1. 个位：2 + 5 = 7，进位0 → 结果个位7
2. 十位：4 + 6 = 10，进位1 → 结果十位0
3. 百位：3 + 4 + 1 = 8，进位0 → 结果百位8

最终结果为[7,0,8] (表示807)，符合预期。

这种方法高效地处理了链表相加的问题，考虑了所有边界情况，包括链表长度不同和最后有进位的情况。