本文已参与「新人创作礼」活动, 一起开启掘金创作之路。
- 两数相加
题目
给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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
题目数据保证列表表示的数字不含前导零
数字转换
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
import java.math.BigInteger;
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
StringBuffer s1 = new StringBuffer();
while (l1!=null){
s1.append(l1.val);
l1=l1.next;
}
StringBuilder s2 = new StringBuilder();
while (l2!=null){
s2.append(l2.val);
l2=l2.next;
}
BigInteger i = new BigInteger(s1.reverse().toString()).add(new BigInteger(s2.reverse().toString()));
String s = new StringBuffer().append(i).reverse().toString();
ListNode head=null;
ListNode temp = null;
for (int i1 = 0; i1 < s.length(); i1++) {
if (i1==0){
temp=head= new ListNode(Integer.parseInt(String.valueOf(s.charAt(i1))));
}else {
temp.next= new ListNode(Integer.parseInt(String.valueOf(s.charAt(i1))));
temp=temp.next;
}
}
return head;
}
}
首先获取链表l1和l2的所有node的val,添加到StringBuffer,应该用StringBuilder的,StringBuilder比StringBuffer性能更好,在非多线程情况下更有优势。
BigInteger i = new BigInteger(s1.reverse().toString()).add(new BigInteger(s2.reverse().toString()));
String s = new StringBuffer().append(i).reverse().toString();
核心部分,将字符串reverse输出并通过BigInteger相加得到结果,然后利用StringBuffer将得到的结果i再次进行字符串reverse。
最后通过循环取出字符串中的字符,放入单向链表中。😀
进位法
/**
* Definition for singly-linked list.
* public class ListNode {
* int val;
* ListNode next;
* ListNode() {}
* ListNode(int val) { this.val = val; }
* ListNode(int val, ListNode next) { this.val = val; this.next = next; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head=null,tail=null;
int carry=0;
while(l1!=null||l2!=null){
int n1 = l1!=null?l1.val:0;
int n2 = l2!=null?l2.val:0;
int sum=n1+n2+carry;
if(head==null){
head=tail=new ListNode(sum%10);
}else{
tail.next=new ListNode(sum%10);
tail=tail.next;
}
carry=sum/10;
if(l1!=null){
l1=l1.next;
}
if(l2!=null){
l2=l2.next;
}
}
if(carry>0){
tail.next=new ListNode(carry);
}
return head;
}
}
过程式语言对每个执行步骤的顺序是有严格的控制的。
接下来我会对每个步骤进行讲解。
while(l1!=null||l2!=null)
循环条件是addTwoNumbers的两个参数不为空,注意这里是短路或!
int n1 = l1!=null?l1.val:0;
int n2 = l2!=null?l2.val:0;
int sum=n1+n2+carry;
这是什么鬼?循环条件是不为空,若为空就进入不了循环了。这里为什么还要判断不为空,很多人看这里会有点懵,这么说吧,若是两个参数给的链表长度不一样会怎么样?所以这里三目运算符的作用就是为了填补链表长度不够的问题。举个例子l1是a->b->c,l2是a->b,但是l2长度是2,所有要在l2后面虚拟一个c值为0。
n1+n2+carry。这里加上了进位。例如:n1=9,n2=9.carry=1。
if(head==null){
head=tail=new ListNode(sum%10);
}else{
tail.next=new ListNode(sum%10);
tail=tail.next;
}
这段代码主要是链表上的Node的添加,sum%10要搞懂,此处求模的意思是求个位数,
carry=sum/10;
求进位,10的进位是1,20的进位是2,28的进位还是2,所以逆推下carry的类型是int!28/10=2.0,int类型会得到2。
但是这题carry永远都是1,题目限制了0 <= Node.val <= 9,最大值是9+9=18。
第一次n1=9,n2=9,carry=0,sum=n1+n2+carry=18,Node.val=sum%10=18%10=8。
第二次n1=9,n2=9,carry=1,sum=19,Node.val=sum%10=9。
第一次是个位,第二次是十位,合起来就是98。
if(l1!=null){
l1=l1.next;
}
if(l2!=null){
l2=l2.next;
}
获取链表的下一个Node。
if(carry>0){
tail.next=new ListNode(carry);
}
第一次,n1=2,n2=5,carry=0,各位为7。
第二次,n1=4,n2=6,carry=1,个位为0。
合起来是07,😈,那第二次的carry就消失了,这句的作用就是补最后的carry,当然carry等于0没必要加了。
正确答案是107。
总结
进位法是我写的第一个,☺有天突发奇想写了个数字转换,但是性能太拉了差了8倍。虽然算法要求的是性能,但是这次我感受到不同思维实现算法的快乐,管他呢快乐万岁!
还没完,有空更新Stream法。