前言
面对困难,许多人望而却步,而成功的人士往往非常清楚,只要敢于和困难拼搏一番,就会发现,困难不过如此!
一、题目内容
给你两个非空的链表,表示两个非负的整数。它们每位数字都是按照逆序的方式存储的,并且每个节点只能存储一位数字。
请你将两个数相加,并以相同形式返回一个表示和的链表。
你可以假设除了数字 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; }
* }
*/
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
}
}
二、解题过程
在看到这道题时,第一想法是逐个遍历链表的节点,将每个节点的数据取出并存储到String类型的数据中,再将这些String类型的字符数据拼接起来反转后再成int类型的数据。之后夭折在了错误的思路上,后面看了相关的解答之后才明白,可以定义一条新的链表按节点来逐个存储两条链表相加的值,最后将这条新的链表返回。
1. 解题思路
创建一条新的链表,设置两个头指针current和result,在两条链表相加的过程中只移动current指针,result指针不移动,最后将result返回。两条链表节点的值在相加时会有进位的情况,因此要进位的数与不进位的数(即剩余的数)进行获取和存储,因此再定义用来存储需要进位的int类型的变量total和存储不需要进位的数值的int类型变量remainder,将两个变量的初值均定义为0。
对两条链表进行遍历,判断条件为当两条链表同时为空时结束循环,确保遍历到了每一条链表的每一个节点。获取需要进位与不需要进位的数值的方法为将两个链表对应节点相加后的值与10进行整除和取余操作,整除后的结果为需要进位的数,取余留下来的数据为不需要进位的数,因此在每一次求和时都会有三个数来相加(即两个链表对应的值和需要进位的数),需要进位的数初值为0,首次参与相加不会对结果产生影响。
将两个链表对应节点值分别存储到int类型数据中是因为如果在相加的过程中有一个链表为空的话就无法进行之后的相加,因此进行求和前先将两个链表的节点值存储到int类型数据中,在存储前进行当前节点是否为空的判断,如果当前节点不为空的话就将当前节点存储的值赋给int类型的数据,如果为空则将int类型的数据赋值为0。
在得到要进位的值total和不需要进位的值remainder后,首先进行新链表是否为空的判断(因为result不进行指针的移动,所以只对result进行非空判断即可),如果此时新链表为空的话,就将不需要进位的值remainder作为链表的含参构造对新链表进行初始化,此时新链表的头节点中存储的数据为不需要进位的值remainder,这样做的原因是为了防止最后返回的链表的头节点中存储着不需要的数据,在之后的判断中都不会再进入新链表为空的执行代码中,因此每一次只需要将不需要进位的值作为链表的含参构造的参数创建新的节点,确保当前节点中存储的值是不需要进位的值 remainder,所以需要进位的值total只参与求和的运算,而不需要进位的值remainder进行的是新链表节点的构造。最后将current指针向后移动,确保此时current指向的节点为null。
在相加与存储到新链表的过程结束后,对链表L1和L2分别进行非空的判断,当L1和L2不为空时向后移动。如果不进行非空的判断,假设L1此时为null,再进行移动时会报空指针异常。
循环结束后,两个链表的节点都进行了相加,但是此时新链表的构造是用不需要进位的值remainder构造的,节点最后一次相加的结果如果需要进位的话这个时候最后一个值是没有存储到新链表当中的,因此需要在循环结束后再对需要进位的值total与0进行一次比较,如果大于0的话表示需要进位,则用total的值做为参数进行节点的含参构造,将最后一个值存储到新链表当中,如果total的值小于0则表明不存在还没有要添加到新链表当中的数据,此时将新链表result返回即可(current永远指向链表的下一个节点,永远为null,因此不将current返回)。
2. 解题代码
代码如下(示例):
/**
* 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) {
// 定义一个链表用来存储两条链表相加的结果,result和current为两个指针
// current为头结点不进行移动,result为最后要输出的链表
ListNode result = null, current = null;
// 定义total变量为l1和l2两条链表相加后需要进位的数据
int total = 0;
// 如果l1和l2两条链表相加后需要进位,remainder变量为个位数的值
// 如果l1和l2两条链表相加后不需要进位,remainder为两数相加后的值
int remainder = 0;
// 只要有一条链表不为空就继续进行遍历
while (l1 != null || l2 != null) {
int num1 = l1 != null ? l1.val : 0;
int num2 = l2 != null ? l2.val : 0;
// 定义sum为两数相加后再与进位数相加后的值
int sum = num1 + num2 + total;
// 对两数相加的结果进行整除,取出进位的值
total = sum / 10;
// 对两数相加的结果对10进行取余,取出不需要进位的值
remainder = sum % 10;
if (result == null) {
result = current = new ListNode(remainder);
} else {
// 将当前不需要进位的值赋值给下一个节点要存储的值
current.next = new ListNode(remainder);
// 将current指针向后移动
current = current.next;
}
// 将l1和l2节点同时向后移动
if (l1 != null) {
l1 = l1.next;
}
if (l2 != null) {
l2 = l2.next;
}
}
if (total > 0) {
current.next = new ListNode(total);
}
return result;
}
}
三、提交结果
总结
以上便是LeetCode第二道题两数相加的解题思路和过程,在解题的过程中我认识到了要灵活的利用链表的特性而不是一味的使用最笨拙的办法去求解,可以转换思路,尝试构造一个新的相同的对象来进行求解。
真正成功的人生,不在于成就的大小,而在于你是否努力地去实现自我,喊出自己的声音,走出属于自己的道路!