两数相加
做445题之前可以先看看2.两数相加
leetcode 445.两数相加 II
1. 题目描述
给你两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储一位数字。将这两数相加会返回一个新的链表。
你可以假设除了数字 0 之外,这两个数字都不会以零开头。
输入:l1 = [7,2,4,3], l2 = [5,6,4]
输出:[7,8,0,7]
2. 题目分析
本题的目的是将两个链表表示的数相加,并用一个新的链表保存,返回新链表的头节点。
注意事项:链表的头节点存放的是数据的最高位,而我们计算时都是从低位开始相加的。所以,必须先把链表翻转!!!
现在的问题便成了,如何反转?
栈和链表反转都可以,也可以使用递归,这里主要介绍链表反转和使用栈来实现~
3. 代码实现
3.1 使用链表反转
可以看看前面写的博客 链表反转
方法:
- 反转链表
l1
,反转链表l2
- 将两链表相加,得到新的链表
l3
- 反转
l3
,返回l3
的头节点
注意进位!!!
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
// 首先进行链表反转
ListNode head1 = reverse(l1);
ListNode head2 = reverse(l2);
ListNode dummy = new ListNode(-1); // 虚拟节点
ListNode cur = dummy;
int carry = 0; // 进位
while (head1 != null || head2 != null) {
// 当head1或者head2为空时,使用head.val 会导致空指针异常
int a = head1 == null ? 0 : head1.val;
int b = head2 == null ? 0 : head2.val;
int sum = a + b + carry; // 两链表的值相加 + 进位
carry = sum >= 10 ? 1 : 0;
cur.next = new ListNode(sum % 10); // 每一个节点保存一个数位
cur = cur.next; // 让cur指向新的节点
if (head1 != null) {
head1 = head1.next;
}
if (head2 != null) {
head2 = head2.next;
}
}
// 当链表都访问完之后,一定要记得判断是否还有进位
if (carry == 1) {
cur.next = new ListNode(1);
}
return reverse(dummy.next); // 一定要记得反转
}
// 反转链表
private ListNode reverse(ListNode head) {
ListNode prev = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = prev;
prev = cur;
cur = next;
}
return prev;
}
复杂度分析:
时间复杂度:
O(n)
,其中n
为l1
长度和l2
长度的最大值。
空间复杂度:O(1)
。返回值不计入。
3.2 使用栈来实现
方法:
- 先将两个链表分别压栈
- 然后一起出栈,计算求和,然后保存的新的链表中
- 使用头插法(避免再一次链表反转)
注意进位!!!
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
Stack<ListNode> stack1 = new Stack<>();
Stack<ListNode> stack2 = new Stack<>();
// 先将链表中的所有元素添加到栈中
while (l1 != null) {
stack1.push(l1);
l1 = l1.next;
}
while (l2 != null) {
stack2.push(l2);
l2 = l2.next;
}
ListNode dummy = null;
int carry = 0; // 进位
// 当两链表都遍历完后,如果carry = 1,还需要进入循环
while (!stack1.empty() || !stack2.empty() || carry != 0) {
int a = stack1.empty() ? 0 : stack1.pop().val;
int b = stack2.empty() ? 0 : stack2.pop().val;
int sum = a + b + carry; // 每次的和应该是对应位相加再加上进位
carry = sum >= 10 ? 1 : 0; // 如果sum大于等于10就进位
ListNode cur = new ListNode(sum % 10);
// 使用头插法
cur.next = dummy;
dummy = cur;
}
return dummy;
}
复杂度分析:
时间复杂度:
O(max(m,n))
,其中m
和n
分别为两个链表的长度。我们需要遍历两个链表的全部位置,而处理每个位置只需要O(1)
的时间。空间复杂度:
O(m+n)
,其中m
和n
分别为两个链表的长度。空间复杂度主要取决于我们把链表内容放入栈中所用的空间。