题目描述
给定两个非空链表来代表两个非负整数。数字最高位位于链表开始位置。它们的每个节点只存储单个数字。将这两数相加会返回一个新链表。
你可以假设除了数字0之外,这两个数字都不会以零开头。
示例1:
输入: listA = [7, 2, 4, 3]; listB = [5, 6, 4]
输出: [7, 8, 0, 7]
解释: 3427 + 4650 = 7807
(通过翻转链表后,采用Leetcode 2 方法进行解题的方式,时间复杂度为O(max(m, n))); 空间复杂度为O(max(m, n))
不允许翻转链表的情况下,需要预先得知链表的相对长度。
思路分析
在这里我们仅仅讨论与Leetcode2不同的部分,Leetcode2主要讨论了左对齐(头节点对其,尾部补0)情况下的加法实现;而这道题是右对齐(尾节点对其,头节点补0)情况下,我们直接讨论如何实现尾节点对齐的情况。
我们依旧先考虑一般情况,当两个链表相等时,我们直接可以进行相加,返回哨兵节点的后置节点即可(这里可参考Leetcode2)。我们直接给出核心代码:
代码1:一般规则下,两链表相加
ListNode pA = listA;
ListNode pB = listB;
ListNode dummyHead = new ListNode(0);
while((pA != null && pB != null) || carry != 0){
sum = (pA == null ? 0 : pA.val)
+ (pB == null ? 0 : pB.val)
+ carry;
curr = sum % 10;
carry = sum / 10;
}
// insert newNode into dummyHead's next
ListNode newNode = new ListNode(curr);
newNode.next = dummyHead.next;
dummyHead.next = newNode;
pA = pA == null ? null : pA.next;
pB = pB == null ? null : pB.next;
接下来我们对边界情况或难点进行进一步梳理:
难点考虑
这里题目已经说明非空链表,故我们不考虑非空链表情况。
当两链表不等时,如何使两链表以右对齐,即尾节点对齐的方式相加?
思考1
思考1:将长链表多出的部分,在短链表处补充ListNode(0)节点
这里详细含义见图1:
图1
我们通过在较短链表listB
链首补全listNode(0)
节点,使得两链表长度相等,之后两数相加过程就为代码1,这里直接给出 节点补全代码与两数相加代码(这里我们称此方法为加式解法):
代码2:加式解法核心代码
ListNode pA = listA;
ListNode pB = listB;
ListNode dummyHead = new ListNode(0);
ListNode assiNodePrev = new ListNode(0);
int carry = 0; // carry bit
int sum;
int curr;
// Step1: equalise length of the two linkedlist
int lenA = 0;
int lenB = 0;
// obtain length of listA and listB
while (pA != null) {
lenA++;
pA = pA.next;
}
while (pB != null) {
lenB++;
pB = pB.next;
}
/* Step2: make them two linkedlist has euqal length */
int deltaNodeNum = lenA - lenB;
if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
assiNodePrev.next = listB;
} else if (deltaNodeNum < 0) {
assiNodePrev.next = listA;
}
// fill vacany nodes with value of O
for (int i = 0; i < Math.abs(deltaNodeNum); i++) {
ListNode node = new ListNode(0);
node.next = assiNodePrev.next;
assiNodePrev.next = node;
}
/* upgrate orientation of pA and pB */
pA = deltaNodeNum < 0 ? assiNodePrev.next : listA;
pB = deltaNodeNum > 0 ? assiNodePrev.next : listB;
/* Step3: add values corrsponding nodes
and
insert active node into <outputList>
*/
while ((pA != null && pB != null) || carry != 0) {
sum = (pA == null ? 0 : pA.val)
+ (pB == null ? 0 : pB.val)
+ carry;
curr = sum % 10;
carry = sum / 10;
// insert newNode into dummyHead's behind
ListNode newNode = new ListNode(curr);
newNode.next = dummyHead.next;
dummyHead.next = newNode;
pA = pA == null ? null : pA.next;
pB = pB == null ? null : pB.next;
}
思考2
思考2:将长链表多出的部分,直接倒序插入哨兵节点后
在思考2中,我们直接将参差不齐的部分插入输出节点,这样会减少一部分计算量以及内内存的消耗,尤其在两链表长度相差较大时性能提升明显。这里相较于思考1的加式解法,我们相当于将多出的“尾巴部分”剪掉了,故这里称为**“减式解法”**,直接给出核心代码有:
代码3:减式解法
/* Step1:
Init. pointers and integers
*/
ListNode pA = listA;//
ListNode pB = listB;//
ListNode dummyHead = new ListNode(0);
ListNode assiNodePrev = new ListNode(0);
int carry = 0; // carry bit
int sum;
int curr;
int lenA = 0;
int lenB = 0;
/* Step2: obtain different section between listA and listB
and
upgrate pA and pB
*/
while (pA != null) {
lenA++;
pA = pA.next;
}
while (pB != null) {
lenB++;
pB = pB.next;
}
// check which is longer one
int deltaNodeNum = lenA - lenB;
if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
assiNodePrev.next = listA;
} else if (deltaNodeNum < 0) {
assiNodePrev.next = listB;
}
// insert nodes which are section longer than other linkelist
// into dummyHead-linkedlist
for (int i = 0; i < Math.abs(deltaNodeNum); i++) {
ListNode node = new ListNode(assiNodePrev.next.val);
node.next = dummyHead.next;
dummyHead.next = node;
assiNodePrev = assiNodePrev.next;
}
// upgrate orientation of pA and pB
pA = deltaNodeNum > 0 ? assiNodePrev.next : listA;
pB = deltaNodeNum < 0 ? assiNodePrev.next : listB;
/* Step3: add values corrsponding nodes
and
insert active node into <outputList>
*/
while ((pA != null && pB != null) || carry != 0) {
sum = (pA == null ? 0 : pA.val)
+ (pB == null ? 0 : pB.val)
+ carry;
curr = sum % 10;
carry = sum / 10;
// insert newNode into dummyHead's behind
ListNode newNode = new ListNode(curr);
newNode.next = dummyHead.next;
dummyHead.next = newNode;
pA = pA == null ? null : pA.next;
pB = pB == null ? null : pB.next;
}
解题代码
public static ListNode solutionWithAdd(ListNode listA, ListNode listB) {
/* Step1:
Init. pointers and integers
*/
ListNode pA = listA;
ListNode pB = listB;
ListNode dummyHead = new ListNode(0);
ListNode assiNodePrev = new ListNode(0);
int carry = 0; // carry bit
int sum;
int curr;
// Step1: equalise length of the two linkedlist
int lenA = 0;
int lenB = 0;
// obtain length of listA and listB
while (pA != null) {
lenA++;
pA = pA.next;
}
while (pB != null) {
lenB++;
pB = pB.next;
}
/* Step2: make them two linkedlist has euqal length */
int deltaNodeNum = lenA - lenB;
if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
assiNodePrev.next = listB;
} else if (deltaNodeNum < 0) {
assiNodePrev.next = listA;
}
// fill vacany nodes with value of O
for (int i = 0; i < Math.abs(deltaNodeNum); i++) {
ListNode node = new ListNode(0);
node.next = assiNodePrev.next;
assiNodePrev.next = node;
}
// upgrate orientation of pA and pB
pA = deltaNodeNum < 0 ? assiNodePrev.next : listA;
pB = deltaNodeNum > 0 ? assiNodePrev.next : listB;
/* Step3: add values corrsponding nodes
and
insert active node into <outputList>
*/
while ((pA != null && pB != null) || carry != 0) {
sum = (pA == null ? 0 : pA.val)
+ (pB == null ? 0 : pB.val)
+ carry;
curr = sum % 10;
carry = sum / 10;
// insert newNode into dummyHead's behind
ListNode newNode = new ListNode(curr);
newNode.next = dummyHead.next;
dummyHead.next = newNode;
pA = pA == null ? null : pA.next;
pB = pB == null ? null : pB.next;
}
return dummyHead.next;
}
public static ListNode solutionWithMinus(ListNode listA, ListNode listB) {
/* Step1:
Init. pointers and integers
*/
ListNode pA = listA;//
ListNode pB = listB;//
ListNode dummyHead = new ListNode(0);
ListNode assiNodePrev = new ListNode(0);
int carry = 0; // carry bit
int sum;
int curr;
int lenA = 0;
int lenB = 0;
/* Step2: obtain different section between listA and listB
and
upgrate pA and pB
*/
while (pA != null) {
lenA++;
pA = pA.next;
}
while (pB != null) {
lenB++;
pB = pB.next;
}
// check which is longer one
int deltaNodeNum = lenA - lenB;
if (deltaNodeNum > 0) { // len(ListA) > len(ListB)
assiNodePrev.next = listA;
} else if (deltaNodeNum < 0) {
assiNodePrev.next = listB;
}
// insert nodes which are section longer than other linkelist
// into dummyHead-linkedlist
for (int i = 0; i < Math.abs(deltaNodeNum); i++) {
ListNode node = new ListNode(assiNodePrev.next.val);
node.next = dummyHead.next;
dummyHead.next = node;
assiNodePrev = assiNodePrev.next;
}
// upgrate orientation of pA and pB
pA = deltaNodeNum > 0 ? assiNodePrev.next : listA;
pB = deltaNodeNum < 0 ? assiNodePrev.next : listB;
/* Step3: add values corrsponding nodes
and
insert active node into <outputList>
*/
while ((pA != null && pB != null) || carry != 0) {
sum = (pA == null ? 0 : pA.val)
+ (pB == null ? 0 : pB.val)
+ carry;
curr = sum % 10;
carry = sum / 10;
// insert newNode into dummyHead's behind
ListNode newNode = new ListNode(curr);
newNode.next = dummyHead.next;
dummyHead.next = newNode;
pA = pA == null ? null : pA.next;
pB = pB == null ? null : pB.next;
}
return dummyHead.next;
}
复杂度分析
不妨设两链表的长度分别为m, n
:
- 加式解法
时间复杂度:我们对数据进行了至少一次的遍历,m + n + 2max(m, n)
, 这里时间复杂度为O(max(m, n))
;
空间复杂度:这里我们需要新建ListNode(0)
节点,且需要新的输出链表,故为abs(m - n) + max(m, n)
, 可知为O(max(m, n)) - 减式解法
时间复杂度:我们对数据进行了至少一次遍历,但对于较短链表并没有额外遍历,m + n + 2min(m, n)
, 这里时间复杂度为O(max(m, n))
;
空间复杂度:这里我们不需要新建ListNode(0)
节点,但依旧需要新的输出链表,故为max(m, n)
, 可知为O(max(m, n))
GitHub源码
完整可运行文件请访问GitHub。