题目描述
- 给出两个非空链表用来表示两个非负整数。其中他们各自的位数按照逆序的方式存储,并且每个节点只存储一位数字。如果将两个数加起来,则返回一个新的链表来表示它的和。
- Example:
Eg1:
(2 -> 4 ->3) + (5 -> 6 -> 4)
(7 -> 0 -> 8)
342 + 465 = 807Eg2:
(1 -> 8) + (0)
(1 -> 8)Eg3:
(9 -> 9) + (1)
(0 -> 0 -> 1)
上面三个用例分别反映了这个问题三个需要注意的点:
Eg1是两个等长的数相加;Eg2是两个不等长的数相加;Eg3是最后一位有进位的情况。
思路
一 整体思路:
进行循环对两个链表的 每一位结点 对应位的值与来自下一位的进位carry(in)相加取余取整操作得到每一位的结果result和进位carry(out)。
(伪 leetCode2.1)
while(){
/* 中间循环体,如下:*/
num1 = p.val;
num2 = q.val;
sum = num1 + num2 +carry;
result = sum%10;
carry = sum/10;
/* 末尾循环体 */
{ ... }
}
二 边界情况(特殊)
1 两个链表不等长: 当其中一个链表走到头之后,只有链表i执行:
(伪 leetcode2.2)
/* 中间循环体,如下:*/
sum = numi + carry;
result = sum%10;
carry = sum/10;
2 最后一位有进位(如:99+1):再新建一个值为carry的结点:
(伪 leetcode2.3)
if(carry != 0)//或if(carry == 1){
{新建一个值为carry的结点}
}
三 细节
1 对每一位结点相加 -> 循环
1.哪种循环: 遍历整个链表显然while(p == null){}语句更常用也更方便)
2.循环要素:
终止条件(条件表达式): 指针指向空结点意味着整个链表遍历结束(p == null)
末尾循环体:指针前进(p = p.next; q = q.next; cur = cur.next;)
2 结果链表使用哑元头结点(dummy head),方便结果返回。
Java Solution
初级版(对思路的整合)
class Solution{ // Solution
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyhead = new ListNode(0); // 结果链表的dummyhead
// 指针
ListNode p = l1; // 初始时刻指向l1链表的第一个结点
ListNode q = l2; // 初始时刻指向l1链表的第一个结点
ListNode cur = dummyhead; // 初始时刻指向l1链表的第一个结点
// 结果暂存变量
int sum = 0; // 求和(num1 + num2 + carry)
int carry = 0; // 进位
// ******************** (伪 leetCode2.1)的实现 ******************* //
while((p != null) || (q != null)) { // 当两个链表至少有一个不为空时 ...
// 两个链表都不为空时,num1+num2+carry(i)
if((p != null)&&(q != null)) {
sum = p.val + q.val + carry;
carry = sum/10;
// 末尾循环体
p = p.next;
q = q.next;
}
// ********* (伪 leetcode2.2) 边界情况1 ******** //
// 当链表1空2不空时,num2+carry(i)
else if((p == null)&&(q != null)) {
sum = q.val + carry;
carry = sum/10;
// 末尾循环体更新
q = q.next;
}
// ##链表2空1不空时,num1+carry(i)
else { // if((p != null)&&(q == null))
sum = p.val + carry;
carry = sum/10;
// 末尾循环体更新
p = p.next;
}
// *********(伪 leetCode2.1 末尾循环体)********** //
cur.next = new ListNode(sum%10);
// 末尾循环体更新
cur = cur.next;
}
// ********** (伪 leetcode2.3) 边界情况2 ********** //
// 两个链表都为空时,看进位是否为空,不空再加一个链表
if (carry != 0) {
cur.next = new ListNode(carry);
}
// 结果为dummyhead后继指针指向的子链表
return dummyhead.next;
}
}
优化版1
利用 x?y:z 运算对边界情况1优化
注意:
初级版程序中使用的是
if{…}else if{…}else{…}
而优化版使用的是 if{…}if{…}
这是因为初级版利用的是分类讨论的思想,每种情况是不能同时发生的,每次只发生其中一种。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode result = new ListNode(0);
ListNode p = l1;
ListNode q = l2;
ListNode curr = result;
int carry = 0;
int sum = 0;
while(p != null || q != null){
int x = (p != null)?p.val:0;
int y = (q != null)?q.val:0;
sum = x + y + carry;
carry = sum/10;
curr.next = new ListNode(sum%10);
curr = curr.next;
// ***** 边界情况1 ********* //
if (p != null){
p = p.next;
}
if (q != null){
q = q.next;
}
}
// ********** 边界情况2 *********** //
if (carry > 0){
curr.next = new ListNode(carry);
}
return result.next;
}
}
优化版2
和优化版1出发点相同
当链表1不为空:链表1值取出并前进指针
当链表2不为空:链表2值取出并前进指针
注意:
初级版程序中使用的是
if{…}else if{…}else{…}
而优化版使用的是 if{…}if{…}
这是因为初级版利用的是分类讨论的思想,每种情况是不能同时发生的,每次只发生其中一种。
class Solution {
public ListNode addTwoNumbers(ListNode l1, ListNode l2) {
ListNode dummyhead = new ListNode(0);
ListNode p = l1;
ListNode q = l2;
ListNode cur = dummyhead;
int sum = 0;
int carry = 0;
while((p != null) || (q != null)) {
sum = carry;
if(p != null){
sum += p.val;
p = p.next;
}
if(q != null){
sum += q.val;
q = q.next;
}
carry = sum/10;
cur.next = new ListNode(sum%10);
cur = cur.next;
}
if (carry != 0) {
cur.next = new ListNode(carry);
}
return dummyhead.next;
}
}