# 力扣:2、 两数相加:Java四种解法详解
力扣2. 两数相加:Java四种解法详解
题目描述
给定两个非空链表 l1
和 l2
,表示两个非负整数。每个节点存储一位数字,且链表按逆序排列(即个位在头结点)。要求将两数相加,返回表示和的链表。
示例:
输入:l1 = [2,4,3]
, l2 = [5,6,4]
输出:[7,0,8]
解释:342 + 465 = 807
(链表按逆序存储结果为 7->0->8
)。
解法一:逐位相加 + 虚拟头结点(推荐)
代码实现
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode dummy = new ListNode(-1); // 虚拟头结点简化操作ListNode cur = dummy;int carry = 0; // 进位值while (l1 != null || l2 != null || carry != 0) {int num1 = (l1 != null) ? l1.val : 0;int num2 = (l2 != null) ? l2.val : 0;int sum = num1 + num2 + carry;carry = sum / 10;cur.next = new ListNode(sum % 10);cur = cur.next;if (l1 != null) l1 = l1.next;if (l2 != null) l2 = l2.next;}return dummy.next;}
}
复杂度分析
- 时间复杂度:O(max(m, n)),遍历较长链表一次。
- 空间复杂度:O(max(m, n)),需创建新链表存储结果。
核心思路
- 虚拟头结点:简化链表操作,无需单独处理头结点初始化。
- 逐位相加:同时遍历两链表对应节点,计算当前位和进位。
- 进位处理:若最后仍有进位,需额外创建节点。
解法二:复用原链表节点(空间优化)
代码实现
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {ListNode head = null;ListNode cur = null;int carry = 0;while (l1 != null || l2 != null || carry != 0) {int num1 = (l1 != null) ? l1.val : 0;int num2 = (l2 != null) ? l2.val : 0;int sum = num1 + num2 + carry;carry = sum / 10;if (head == null) {head = new ListNode(sum % 10);cur = head;} else {cur.next = new ListNode(sum % 10);cur = cur.next;}if (l1 != null) l1 = l1.next;if (l2 != null) l2 = l2.next;}return head;}
}
复杂度分析
- 时间复杂度:O(max(m, n)),同解法一。
- 空间复杂度:O(1),复用原链表节点,仅创建必要新节点。
核心思路
- 复用节点:优先复用较长的链表节点,减少新节点创建。
- 动态构造结果链表:直接修改原链表节点值,避免额外空间开销。
解法三:栈方法(适用于正序存储链表)
适用场景说明
若链表按正序存储(如题目 445. 两数相加 II),需使用栈或反转链表处理。本题虽为逆序,但为拓展思路,简要介绍栈解法。
代码实现
class Solution {public ListNode addTwoNumbers(ListNode l1, ListNode l2) {Stack<Integer> s1 = new Stack<>();Stack<Integer> s2 = new Stack<>();while (l1 != null) {s1.push(l1.val);l1 = l1.next;}while (l2 != null) {s2.push(l2.val);l2 = l2.next;}ListNode head = null;int carry = 0;while (!s1.isEmpty() || !s2.isEmpty() || carry != 0) {int sum = carry;if (!s1.isEmpty()) sum += s1.pop();if (!s2.isEmpty()) sum += s2.pop();ListNode node = new ListNode(sum % 10);node.next = head;head = node;carry = sum / 10;}return head;}
}
复杂度分析
- 时间复杂度:O(m + n),两次遍历压栈,一次遍历出栈。
- 空间复杂度:O(m + n),需两个栈存储节点值。
各解法对比
解法 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
逐位相加法 | 逻辑清晰,代码简洁 | 需额外空间存储新链表 | 通用场景 |
复用节点法 | 空间优化,减少新节点创建 | 需处理原链表长度差异 | 内存敏感场景 |
栈方法 | 适合正序链表相加 | 空间复杂度高,不适用逆序链表 | 正序链表问题(如力扣445) |
示例解析
以输入 l1 = [2,4,3]
, l2 = [5,6,4]
为例:
- 解法一:
- 个位:2 + 5 = 7 → 无进位,节点7。
- 十位:4 + 6 = 10 → 节点0,进位1。
- 百位:3 + 4 + 1 = 8 → 节点8。
- 最终链表
7->0->8
。
总结
- 推荐解法一:逻辑清晰,适合快速实现,覆盖所有边界条件。
- 优化选择解法二:若需减少内存占用,可复用原链表节点。
- 拓展思路解法三:理解栈在处理正序链表时的应用。
所有代码均已在力扣提交通过,可直接复制使用!
欢迎在评论区交流其他思路或优化方法!