两数相加原题地址
方法一:模拟
注意到链表的方向是从低位到高位,而做“竖式相加”也是低位到高位。
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 <- 8int 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;}
};