今天遇到LeetCode第2题"两数相加",题目要求处理两个非空链表,它们以逆序方式存储非负整数(即链表头是个位,第二位是十位,以此类推)。需要将这两个数相加,并返回同样格式的链表。比如输入(2->4->3)和(5->6->4),对应数字342+465=807,输出应为7->0->8。
我第一反应是用数组处理,因为对数组操作更熟悉。思路很直接:
这种方法的优点是思路直观,数组索引天然对应数位顺序。但实际编码时遇到了两个关键问题:
对于不同长度的数组,需要处理数组越界问题。我的解决方案是:
python复制max_len = max(len(arr1), len(arr2))
sum_arr = []
carry = 0 # 进位标志
for i in range(max_len):
# 获取当前位的值,如果数组已越界则取0
val1 = arr1[i] if i < len(arr1) else 0
val2 = arr2[i] if i < len(arr2) else 0
total = val1 + val2 + carry
carry = total // 10 # 计算进位
sum_arr.append(total % 10) # 存储当前位结果
# 处理最后的进位
if carry > 0:
sum_arr.append(carry)
关键点:必须考虑最后可能存在的进位(如999+1=1000的情况),这个很容易被忽略。
这里学到了一个实用的链表处理技巧——使用dummy node(虚拟头节点):
python复制class ListNode:
def __init__(self, val=0, next=None):
self.val = val
self.next = next
def array_to_list(sum_arr):
dummy = ListNode(0) # 创建虚拟头节点
curr = dummy # 当前指针
for num in sum_arr:
curr.next = ListNode(num) # 创建新节点并链接
curr = curr.next # 移动指针
return dummy.next # 返回真实头节点
虚拟头节点的妙处在于:
虽然数组解法可行,但题目明确给出链表结构,直接操作链表更符合题意。经过复习链表操作后,我实现了更优雅的解法:
python复制def addTwoNumbers(l1: ListNode, l2: ListNode) -> ListNode:
dummy = ListNode(0)
curr = dummy
carry = 0
while l1 or l2 or carry:
val1 = l1.val if l1 else 0
val2 = l2.val if l2 else 0
total = val1 + val2 + carry
carry = total // 10
curr.next = ListNode(total % 10)
curr = curr.next
l1 = l1.next if l1 else None
l2 = l2.next if l2 else None
return dummy.next
该解法时间复杂度为O(max(m,n)),其中m和n分别是两个链表的长度。空间复杂度也是O(max(m,n))(用于存储结果链表)。这是最优解法,因为必须遍历每个节点至少一次。
忘记处理最后进位:
python复制# 错误示例
while l1 or l2: # 缺少carry判断
...
当两链表都结束时若carry=1,会导致结果缺少最高位
指针移动错误:
python复制# 错误示例
l1 = l1.next # 未检查l1是否为None
应该使用l1 = l1.next if l1 else None
虚拟头节点使用不当:
python复制return dummy # 错误!应该返回dummy.next
打印链表工具函数:
python复制def print_list(node):
while node:
print(node.val, end=" -> ")
node = node.next
print("None")
测试用例设计:
可视化调试:
在纸上画出链表结构和指针变化,特别有助于理解dummy node的工作机制。
如果不允许创建新链表,可以复用较长的输入链表:
python复制def addTwoNumbers(l1, l2):
# 先计算两链表长度
len1, len2 = 0, 0
p1, p2 = l1, l2
while p1: len1 += 1; p1 = p1.next
while p2: len2 += 1; p2 = p2.next
# 确保l1是较长的链表
if len1 < len2:
l1, l2 = l2, l1
dummy = ListNode(0)
dummy.next = l1
carry = 0
# 第一阶段:两个链表都有节点
while l2:
l1.val += l2.val + carry
carry = l1.val // 10
l1.val %= 10
l1 = l1.next
l2 = l2.next
# 第二阶段:仅剩l1和进位
while l1 and carry:
l1.val += carry
carry = l1.val // 10
l1.val %= 10
l1 = l1.next
# 处理最后的进位
if carry:
curr = dummy.next
while curr.next:
curr = curr.next
curr.next = ListNode(carry)
return dummy.next
思考题:如果链表头存储最高位数字(如1->2->3表示123),如何解决?
解决方案:
python复制def reverseList(head):
prev = None
while head:
next_node = head.next
head.next = prev
prev = head
head = next_node
return prev
def addTwoNumbersForward(l1, l2):
l1 = reverseList(l1)
l2 = reverseList(l2)
res = addTwoNumbers(l1, l2)
return reverseList(res)
这种变换展示了算法思维的灵活性——通过预处理将新问题转化为已知问题。
通过这道题,我总结了几个链表处理的黄金法则:
链表操作的核心在于理解指针的指向关系。这道看似简单的题目,实际上涵盖了链表处理的多个关键技巧,是非常好的练习题。