一、题目描述
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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
- 题目数据保证列表表示的数字不含前导零
二、解题思路
- 创建一个新的链表,用于存储两个链表相加的结果。
- 初始化一个变量
carry
用于存储进位。 - 创建一个指针
dummy
指向新链表的头节点,以及一个指针current
用于指向当前要添加的节点。 - 同时遍历两个链表
l1
和l2
,直到其中一个链表遍历完毕。 - 在遍历过程中,计算两个当前节点的值加上进位
carry
的和。 - 更新
carry
为和的十位数部分。 - 创建一个新的
ListNode
,其值为和的个位数部分,并将其连接到current.next
。 - 更新
current
为新创建的节点。 - 如果两个链表都遍历完毕,检查
carry
是否不为0,如果是,创建一个新的ListNode
作为结果链表的尾节点。 - 返回
dummy.next
,即结果链表的头节点。
三、具体代码
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummy = new ListNode(0);
ListNode current = dummy;
int carry = 0;
while (l1 != null || l2 != null) {
int x = (l1 != null) ? l1.val : 0;
int y = (l2 != null) ? l2.val : 0;
int sum = carry + x + y;
carry = sum / 10;
int digit = sum % 10;
current.next = new ListNode(digit);
current = current.next;
if (l1 != null) l1 = l1.next;
if (l2 != null) l2 = l2.next;
}
if (carry > 0) {
current.next = new ListNode(carry);
}
return dummy.next;
}
}
四、时间复杂度和空间复杂度
1. 时间复杂度
- 时间复杂度是 O(max(m, n)),其中 m 和 n 分别是两个输入链表的长度。
- 这是因为算法需要遍历两个链表,直到其中一个链表的末尾。
- 在最坏的情况下,算法将遍历两个链表中的所有节点。
2. 空间复杂度
- 空间复杂度是 O(max(m, n)),因为我们需要为结果链表创建新的节点。
- 在最坏的情况下,如果两个链表都很大,结果链表的长度可能会接近两个输入链表的长度之和。
- 这里假设进位不会超过一位,所以空间复杂度不会超过输入链表的长度。
五、总结知识点
-
链表操作:理解链表的基本结构,包括如何创建新节点、如何遍历链表以及如何连接节点。
-
递归与迭代:虽然这个问题通常使用迭代解决,但理解递归思维对于解决链表问题也是有帮助的,尤其是在处理链表的反转、合并等操作时。
-
进位处理:在处理两个大数相加时,需要正确处理进位。这涉及到基本的算术运算和逻辑判断。
-
条件判断:使用三元运算符(
? :
)进行条件判断,以确定在两个链表的节点都存在或只有一个存在时的值。 -
指针操作:使用
current
指针来跟踪链表的当前位置,并在每次迭代中更新它以指向新创建的节点。 -
链表头节点:使用一个哑节点(dummy head)来简化链表的头部操作,这样可以统一处理链表为空的情况。
以上就是解决这个问题的详细步骤,希望能够为各位提供启发和帮助。