题目:给你两个 非空 的链表,表示两个非负的整数。它们每位数字都是按照 逆序 的方式存储的,并且每个节点只能存储 一位 数字。请你将两个数相加,并以相同形式返回一个表示和的链表。你可以假设除了数字 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
题目数据保证列表表示的数字不含前导零
下面是官方给出的解题模板,ListNode类的方法及属性的修饰符都是默认的,所以要和自己的测试类在一个包下(和测试类的位置关系:其他类同一个包)
/**
* 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){
}
}
这道题对我而言比较难。。。
解题开始
自己做起来有些费劲,参考的官方题解,如下:
我的思路分析:一般情况下,两个整数相加,我们会采取右对齐的方式,先从个位开始相加,如果和大于10,则进位。而现在由两个链表表示的两个整数是逆序排列的,所以现在要将两个链表左对齐,最左边是个位,将链表对应位置上下两个数字相加,如果大于10,则进位。
程序实现方式:遍历两个链表,每次循环将链表上下两个数字相加,两数和的链表当前位置元素是:(l1.val + l2.val + carry) % 10
; 下次循环进位carry为:(l1.val + l2.val + carry) / 10
; carry 在循环外初始化为0,每次循环会给carry重新赋值,如果两个链表长度不一样,则可以认为短的一方链表后面有若干个0。如果循环结束,carry>0,需要将carry作为答案链表的子节点。
class Solution {
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode head = null, tail = null;
int carry = 0; // 初始化进位值
while (l1 != null || l2 != null) {
int l1val = l1 == null ? 0 : l1.val; // 如果l1链表为null,就给l1链表后面补零
int l2val = l2 == null ? 0 : l2.val;
int sum = l1val + l2val + carry; // l1、l2两个链表对应位置的值 + 进位值 的 和
int cur = sum % 10; // 答案链表当前位置的值
carry = sum / 10; // 新的进位值
if (head == null) { // 这个if else不好理解
// 初始化head、tail,只执行一次,此时head和tail指向同一个地址
head = tail = new ListNode(cur);
} else { // 每执行完一次else,相当于给head对象添加一个新的next子节点,
// 而tail每次都被赋予新的地址,执行完第一个else后,上面if中初始化后的地址只指向head对象
tail.next = new ListNode(cur); // tail和 head的最后一个next节点 指向同一个地址;head每次产生新的next子节点
tail = tail.next; // 指向下一个节点
}
if (l1 != null) { // 防止空指针
l1 = l1.next; // 指向链表下一个节点,作为while循环退出的条件之一
}
if (l2 != null) {
l2 = l2.next;
}
}
if (carry > 0) { //如果carry>0说明中间没有把进位消化掉,carry到了末尾还大于0就必须要进位了。
tail.next = new ListNode(carry);
}
return head;
}
public static void main(String[] args) {
// ListNode l1 = new ListNode(1, new ListNode(2, new ListNode(3, new ListNode(4, new ListNode(3, new ListNode(7))))));
// ListNode l2 = new ListNode(5, new ListNode(6, new ListNode(4, new ListNode(9, new ListNode(3, new ListNode(7))))));
ListNode l1 = new ListNode(2,new ListNode(4,new ListNode(3)));
ListNode l2 = new ListNode(5,new ListNode(6,new ListNode(4)));
ListNode l3 = addTwoNumbers(l1, l2);
while (l3 != null) { // 测试结果,遍历链表
System.out.println(l3.val);
l3 = l3.next;
}
}
}
复杂度分析
- 时间复杂度:O(max(m,n))
- 空间复杂度:O(1)
时间复杂度:
自己做这题的第一版,硬着头皮写下的,也可以测试通过。不过,这好像和算法不怎么沾边,不好。如下:
// 需要再导包:import java.math.BigDecimal;
public static ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode l3 = null;
StringBuilder sb1 = new StringBuilder();
StringBuilder sb2 = new StringBuilder();
while(l1 != null){
sb1.append(l1.val);
l1 = l1.next;
}
while(l2 != null){
sb2.append(l2.val);
l2 = l2.next;
}
BigDecimal l1Sum = new BigDecimal(sb1.reverse().toString()); // 一开始这里是Integer,通过测试十几个,后面换为Long,通过了90%,最后换成BigDecimal后,100%通过。和整数的精度有关系。
BigDecimal l2Sum = new BigDecimal(sb2.reverse().toString());
BigDecimal l3Sum = l1Sum.add(l2Sum);
String s = l3Sum.toString();
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
String s1 = String.valueOf(c);
int num = Integer.parseInt(s1);
l3 = new ListNode(num,l3);
}
return l3;
}