题解:LeetCode - 2 - 两数相加
题目描述:
给出两个非空的链表用来表示两个非负的整数。其中,它们各自的位数是按照逆序的方式存储的,并且它们的每个节点只能存储一位数字。
如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。
您可以假设除了数字 0 之外,这两个数都不会以 0 开头。
示例:
输入:(2 -> 4 -> 3) + (5 -> 6 -> 4)
输出:7 -> 0 -> 8
原因:342 + 465 = 807
解法一:整数法
整数法的思路很简单,将整个链的数字都读出来进行字符串拼接,再转换成大整数进行相加,这样就无需考虑两个链的长度以及进位问题,最后分离各个数位,拆分成单个,返回第一个结点。
import java.math.BigInteger;
public class Q2 {
public static void main(String[] args) {
// 9
ListNode l1_0 = new ListNode(9);
// 9999999991
ListNode l2_0 = new ListNode(1);
ListNode l2_1 = new ListNode(9);
ListNode l2_2 = new ListNode(9);
ListNode l2_3 = new ListNode(9);
ListNode l2_4 = new ListNode(9);
ListNode l2_5 = new ListNode(9);
ListNode l2_6 = new ListNode(9);
ListNode l2_7 = new ListNode(9);
ListNode l2_8 = new ListNode(9);
ListNode l2_9 = new ListNode(9);
l2_0.next = l2_1;
l2_1.next = l2_2;
l2_2.next = l2_3;
l2_3.next = l2_4;
l2_4.next = l2_5;
l2_5.next = l2_6;
l2_6.next = l2_7;
l2_7.next = l2_8;
l2_8.next = l2_9;
Q2 q2 = new Q2();
ListNode result = q2.addTwoNumbers(l1_0, l2_0);
System.out.println(result.next.val);
System.out.println(result.next.next.val);
}
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode temp1 = l1;
ListNode temp2 = l2;
String num1 = "";
String num2 = "";
while(temp1 != null) {
num1 = temp1.val + num1;
temp1 = temp1.next;
}
while(temp2 != null) {
num2 = temp2.val + num2;
temp2 = temp2.next;
}
BigInteger n1 = new BigInteger(num1.trim());
BigInteger n2 = new BigInteger(num2.trim());
BigInteger sum = n1.add(n2);
BigInteger ten = new BigInteger("10");
ListNode result = new ListNode(Integer.valueOf(sum.mod(ten) + ""));
ListNode extra = result;
int length = String.valueOf(sum).length() - 1;
int count = 0;
while(count < length) {
count ++;
sum = sum.divide(ten);
ListNode temp = new ListNode(Integer.valueOf(sum.mod(ten) + ""));
extra.next = temp;
extra = temp;
}
return result;
}
}
class ListNode{
int val;
ListNode next;
public ListNode(int x) {
val = x;
}
}
解法二:初等数学
思路:
我们使用变量来跟踪进位,并从包含最低有效位的表头开始模拟逐位相加的过程。
图1,对两数相加方法的可视化:,每个结点都包含一个数字,并且数字按位逆序存储。
就像你在纸上计算两个数字的和那样,我们首先从最低有效位也就是列表和
的表头开始相加。由于每位数字都应当处于
的范围内,我们计算两个数字的和时可能会出现“溢出”。例如,
。在这种情况下,我们会将当前位的数值设置为
,并将进位
带入下一次迭代。进位
必定是
或
,这是因为两个数字相加(考虑到进位)可能出现的最大和为
。
伪代码如下:
- 将当前结点初始化为返回列表的哑结点。
- 将进位
初始化为
。
- 将
和
分别初始化为列表
和
的头部。
- 遍历列表
和
直至到达它们的尾端。
- 将
设为结点
的值。如果
已经到达
的末尾,则将其值设置为
。
- 将
设为结点
的值。如果
已经到达
的末尾,则将其值设置为
。
- 设定
。
- 更新进位的值,
。
- 创建一个数值为
的新结点,并将其设置为当前结点的下一个结点,然后将当前结点前进到下一个结点。
- 同时,将
和
前进到下一个结点。
- 将
- 检查
是否成立,如果成立,则向返回列表追加一个含有数字
的新结点。
- 返回哑结点的下一个结点。
请注意,我们使用哑结点来简化代码。如果没有哑结点,则必须编写额外的条件语句来初始化表头的值。
public class Q2 {
public static void main(String[] args) {
// 9
ListNode l1_0 = new ListNode(9);
// 9999999991
ListNode l2_0 = new ListNode(1);
ListNode l2_1 = new ListNode(9);
ListNode l2_2 = new ListNode(9);
ListNode l2_3 = new ListNode(9);
ListNode l2_4 = new ListNode(9);
ListNode l2_5 = new ListNode(9);
ListNode l2_6 = new ListNode(9);
ListNode l2_7 = new ListNode(9);
ListNode l2_8 = new ListNode(9);
ListNode l2_9 = new ListNode(9);
l2_0.next = l2_1;
l2_1.next = l2_2;
l2_2.next = l2_3;
l2_3.next = l2_4;
l2_4.next = l2_5;
l2_5.next = l2_6;
l2_6.next = l2_7;
l2_7.next = l2_8;
l2_8.next = l2_9;
Q2 q2 = new Q2();
ListNode result = q2.addTwoNumbers(l1_0, l2_0);
System.out.println(result.next.val);
System.out.println(result.next.next.val);
}
public ListNode addTwoNumber(ListNode l1, ListNode l2) {
ListNode header = new ListNode(0);
ListNode p = l1, q = l2, curr = header;
int carry = 0;
while(p != null || q != null) {
int x = (p != null)? p.val: 0;
int y = (q != null)? q.val: 0;
int sum = carry + x + y;
carry = sum / 10;
curr.next = new ListNode(sum % 10);
curr = curr.next;
if(p != null) {
p = p.next;
}
if(q != null) {
q = q.next;
}
}
if(carry > 0) {
curr.next = new ListNode(carry);
}
return header.next;
}
}
class ListNode{
int val;
ListNode next;
public ListNode(int x) {
val = x;
}
}
复杂度分析:
-
时间复杂度:
,假设
和
分别表示
和
的长度,上面的算法最多重复
次。
-
空间复杂度:
, 新列表的长度最多为
。