【力扣】两数相加,模拟+递归

两数相加原题地址

方法一:模拟

注意到链表的方向是从低位到高位,而做“竖式相加”也是低位到高位。

   1 2 3

+    4 5

-----------

   1 6 8

所以可以用同样的方法来模拟。如果不考虑进位,只需要取出对应位的2个数相加,再尾插到新的链表中。

【力扣】两数相加,模拟+递归_第1张图片

注意新的链表也是从低位到高位,也就是按照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)构成的链表。这么说有点抽象,看图:

【力扣】两数相加,模拟+递归_第2张图片

【力扣】两数相加,模拟+递归_第3张图片

递归结束的条件是什么呢?自然是两个链表都走完了,而且没有进位。

// 方法二:递归
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;
    }
};

你可能感兴趣的:(算法)