02两数相加.2
题目描述
给出两个非空链表用来表示两个非负整数
其中它们各自的位数是按照逆序的方式存储的,并且它们的每个节点只能存储一位数字
如果我们将这两个数相加起来,则会返回一个新的链表来表示它们的和
您可以假设除了数字0之外,这两个数都不会以0开头
1. Comprehend 理解题意
理解题意
- 一个链表表示一个整数
- 每个节点存储一个数字
- 各自的位数逆序存储
- 链表相加,即链表表示的整数相加
- 返回链表表示的两数之和
细节问题:
- 两个链表都是非空的
- 两个非负整数
- 两个数字都不会以0开头
- 数字逆序排列,第一个节点表示整数的最低位
- 【重点】题目没有明确整数的范围,这是个容易忽略的点
解题思路
解法—:分别将链表转成数字,再相加
解法二:直接将对应位置数字相加
2.Choose 数据结构及算法思维选择
链表
-
一种物理存储上非连续、非顺序的存储结构
-
由一系列节点组成,可以在运行时动态生成
-
每个结点包括两个部分:数据域;指针域
链表的特点
- 数据存储不要求连续空间,不限制容量
- 数据的逻辑顺序通过指针链接次序实现
- 从链表头部依次访问后面的节点
- 在链表表头插入数据的时间复杂度是O(1)
class ListNode {
int val;
ListNode next;
ListNode() {};
ListNode(int x) {
val = x;
}
}
数据结构的选择
链表
算法思维的选择
解法一:分别将链表转成数字,再相加
遍历
解法二:直接将对应位置数字相加
数学
3.Code 基本解法及编码实现
解法一:暴力解法
- 遍历两个链表使用数学思维分别将他们转成整数
- 对两个整数进行求和得到sum
- 将sum按照数学思维再转成链表
解法一:暴力解法边界和细节问题
边界问题
- 链表转整数时,next==null结尾
- 整数转链表,处理完最高位:value==0
细节问题
- 不同节点代表不同的位数
- 题目没有明确整数范围:
int 溢出可以用 long,long 溢出?
三. Code 基本解法及编码实现
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
long number1 = 0;
int digit = 0;
//将链表转成数
while (l1 != null) {
int value = l1.val;
number1 = number1 + (int) (value * Math.pow(10, digit));
l1 = l1.next;
digit++;
}
long number2 = 0;
digit = 0;
//将链表转成数
while (l2 != null) {
int value = l2.val;
number2 = number2 + (int) (value * Math.pow(10, digit));
l2 = l2.next;
digit++;
}
//求和
long sum = number1 + number2;
ListNode resultHead = new ListNode(-1);
ListNode currentNode = resultHead;
//如果等于0,则构造List(0)
if (sum == 0) {
currentNode.next = new ListNode(0);
}
while (sum != 0) {
int val = (int) (sum % 10);
sum = sum / 10;
currentNode.next = new ListNode(val);
currentNode = currentNode.next;
}
return resultHead.next;
}
}
时间复杂度与空间复杂度
时间复杂度
-
分别遍历两个链表:O(m + n)
-
将两数字的和转成链表,在链表尾部插入节点:
O(k)=O(max(m, n))
PS: k=max(m, n)或 k=max(m, n)+1
-
总复杂度:O(m+n+max(m,n)) 近似于O(m+n)
空间复杂度
- 创建一个新链表,其长度取决于两数之和的位数:O(max(m, n))
4.Consider 思考更优解
- 剔除无效代码或优化空间消耗
- 减少循环层级和次数
- 优化代码
- 寻找更好的算法思维
- 能否跳过链表转整数的过程?
- 可否直接将链表对应位置数字相加?
- 借鉴其它算法
5.Code 最优解思路及编码实现
最优解:数学思维解法
- 遍历两个链表
- 对应位置的节点数值相加
- 将结果插入新链表尾部 大于10,则进位,将进位加到下个节点
最优解:边界和细节问题
边界问题
- 两个链表边界:next==null
细节问题
- 两个链表长度不一致,短链表高位视为0
- 链表最高位发生进位,结果链表需要增加一 个节点存放进位数字
Code 最优解思路及编码实现
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode resultHead = new ListNode(-1);
ListNode currentNode = resultHead;
ListNode number01 = l1;
ListNode number02 = l2;
int carry = 0;
while (number01 != null || number02 != null) {
//如果当前为尾节点,则为0
int num1 = number01 != null ? number01.val : 0;
int num2 = number02 != null ? number02.val : 0;
int sum = num1 + num2 + carry;
//得到进位
carry = sum / 10;
//得到当前位
sum = sum % 10;
//创建新节点,
currentNode.next = new ListNode(sum);
currentNode = currentNode.next;
//如果当前指针为null,则不移动,指针不为null,则移动到下一个节点
number01 = number01 != null ? number01.next : number01;
number02 = number02 != null ? number02.next : number02;
}
//如果有进位
if (carry > 0) {
currentNode.next = new ListNode(carry);
}
return resultHead.next;
}
}
时间复杂度及空间复杂度
时间复杂度:O(max(m,n))
- 两个链表的加法运算: O(max(m,n))
- 与暴力解法的O(m+n)差别不大, 但大数运算以及循环次数来看, 更优
空间复杂度:O(max(m,n))
- 创建需要创建一个新链表: O(max(m, n))
6.Change 变形延伸
- 6C解题法
- 链表的特点
- 数据存储不要求连续空间,不限制容量
- 数据的逻辑顺序通过指针链接次序实现
- 从链表头部依次访问后面的节点
- 在链表表头插入数据的时间复杂度是O(1)
- 数学思维
- 数学竖式