两数相加原题地址
方法一:模拟
注意到链表的方向是从低位到高位,而做“竖式相加”也是低位到高位。
1 2 3
+ 4 5
-----------
1 6 8
所以可以用同样的方法来模拟。如果不考虑进位,只需要取出对应位的2个数相加,再尾插到新的链表中。
注意新的链表也是从低位到高位,也就是按照8->6->1的方向存储。链表中不存在前置0,所以当其中一个链表遍历完了,另一个链表没遍历完的时候,遍历完的链表剩下的元素都当做前置0来处理。
实际计算时,还会出现进位的情况,所以需要一个变量carry来存储进位值,这个变量要定义在循环外面,因为下一次进入循环后不能销毁,计算时需要加上进位值。最终需要存储在新链表的元素就是(n1+n2+carry)mod10。其中n1和n2为两个链表中取出来的值,如果其中一个链表走到头了当做前置0处理。另外,新的进位值为(n1+n2+carry)/10。
需要注意尾插时对于新链表是否为空的分类讨论。如果新链表为空,需要对头结点重新赋值,否则直接在尾部插入即可。为了防止尾插时的找尾操作效率太低,最好定义一个尾指针存储尾部的节点地址。
最后一次循环走完后,如果carry不为0,记得还需在尾部插入一个1,这点非常容易出错。
// 方法一:模拟
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
// 由于要反复尾插,最好定义一个指针存储尾节点的地址
ListNode* head = nullptr, * tail = nullptr;
int carry = 0; // 进位,必须定义在循环外面,下次循环还能用
// 两条链表都走完才结束
while (l1 || l2)
{
// 如果链表走完了,剩下的数为前置0
// 1 <- 2 <- 3 <- 4 <- 5
// 0 <- 0 <- 6 <- 7 <- 8
int n1 = l1 ? l1->val : 0;
int n2 = l2 ? l2->val : 0;
int sum = n1 + n2 + carry;
carry = sum / 10;
// 尾插,是否为空链表要分类讨论
if (!head)
{
head = tail = new ListNode(sum % 10);
}
else
{
tail->next = new ListNode(sum % 10);
tail = tail->next;
}
if (l1)
{
l1 = l1->next;
}
if (l2)
{
l2 = l2->next;
}
}
if (carry)
{
tail->next = new ListNode(1);
}
return head;
}
};
方法二:递归
对于链表的题目,自然会想到递归。根据模拟法的思路,影响插入元素的因素为两个链表取出来的值以及进位值,及(l1, l2, carry)。如果要改为递归实现,我们需要把问题转换为:当前需要插入的值(n1+n2+carry)mod10,以及子问题(当前需要插入的值后面需要插入什么呢?)
子问题的解决很简单,只需要在当前节点后面插入由子问题(l1的下一个节点, l2的下一个节点, carry)构成的链表。这么说有点抽象,看图:
递归结束的条件是什么呢?自然是两个链表都走完了,而且没有进位。
// 方法二:递归
class Solution {
public:
ListNode* addTwoNumbers(ListNode* l1, ListNode* l2) {
return _addTwoNumbers(l1, l2, 0);
}
ListNode* _addTwoNumbers(ListNode* l1, ListNode* l2, int carry)
{
// 链表都为空且无进位,终止递归
if (!l1 && !l2 && carry == 0)
{
return nullptr;
}
// 链表非空,则取值相加
int n1 = 0;
int n2 = 0;
if (l1)
{
n1 = l1->val;
l1 = l1->next;
}
if (l2)
{
n2 = l2->val;
l2 = l2->next;
}
int sum = n1 + n2 + carry;
carry = sum / 10;
// 链接上新的节点,继续递归至下一层
ListNode* node = new ListNode(sum % 10);
node->next = _addTwoNumbers(l1, l2, carry);
return node;
}
};