# 0002. 两数相加 ## 题目地址(2. 两数相加) <https://leetcode-cn.com/problems/add-two-numbers/> ## 题目描述 ``` <pre class="calibre18">``` 给出两个 非空 的链表用来表示两个非负的整数。其中,它们各自的位数是按照 逆序 的方式存储的,并且它们的每个节点只能存储 一位 数字。 如果,我们将这两个数相加起来,则会返回一个新的链表来表示它们的和。 您可以假设除了数字 0 之外,这两个数都不会以 0 开头。 示例: 输入:(2 -> 4 -> 3) + (5 -> 6 -> 4) 输出:7 -> 0 -> 8 原因:342 + 465 = 807 ``` ``` ## 前置知识 - 链表 ## 公司 - 阿里 - 百度 - 腾讯 ## 思路 设立一个表示进位的变量 carried,建立一个新链表, 把输入的两个链表从头往后同时处理,每两个相加,将结果加上 carried 后的值作为一个新节点到新链表后面。 ![](https://img.kancloud.cn/cd/2a/cd2a9ec008332c2913cbe3e9c5b2737e_953x528.gif) (图片来自: <https://github.com/MisterBooo/LeetCodeAnimation>) ## 关键点解析 1. 链表这种数据结构的特点和使用 2. 用一个 carried 变量来实现进位的功能,每次相加之后计算 carried,并用于下一位的计算 ## 代码 - 语言支持:JS,C++ JavaScript: ``` <pre class="calibre18">``` <span class="hljs-title">/** * Definition for singly-linked list. * function ListNode(val) { * this.val = val; * this.next = null; * } */</span> <span class="hljs-title">/** * @param {ListNode} l1 * @param {ListNode} l2 * @return {ListNode} */</span> <span class="hljs-keyword">var</span> addTwoNumbers = <span class="hljs-function"><span class="hljs-keyword">function</span> (<span class="hljs-params">l1, l2</span>) </span>{ <span class="hljs-keyword">if</span> (l1 === <span class="hljs-params">null</span> || l2 === <span class="hljs-params">null</span>) <span class="hljs-keyword">return</span> <span class="hljs-params">null</span>; <span class="hljs-title">// 使用dummyHead可以简化对链表的处理,dummyHead.next指向新链表</span> <span class="hljs-keyword">let</span> dummyHead = <span class="hljs-keyword">new</span> ListNode(<span class="hljs-params">0</span>); <span class="hljs-keyword">let</span> cur1 = l1; <span class="hljs-keyword">let</span> cur2 = l2; <span class="hljs-keyword">let</span> cur = dummyHead; <span class="hljs-title">// cur用于计算新链表</span> <span class="hljs-keyword">let</span> carry = <span class="hljs-params">0</span>; <span class="hljs-title">// 进位标志</span> <span class="hljs-keyword">while</span> (cur1 !== <span class="hljs-params">null</span> || cur2 !== <span class="hljs-params">null</span>) { <span class="hljs-keyword">let</span> val1 = cur1 !== <span class="hljs-params">null</span> ? cur1.val : <span class="hljs-params">0</span>; <span class="hljs-keyword">let</span> val2 = cur2 !== <span class="hljs-params">null</span> ? cur2.val : <span class="hljs-params">0</span>; <span class="hljs-keyword">let</span> sum = val1 + val2 + carry; <span class="hljs-keyword">let</span> newNode = <span class="hljs-keyword">new</span> ListNode(sum % <span class="hljs-params">10</span>); <span class="hljs-title">// sum%10取模结果范围为0~9,即为当前节点的值</span> carry = sum >= <span class="hljs-params">10</span> ? <span class="hljs-params">1</span> : <span class="hljs-params">0</span>; <span class="hljs-title">// sum>=10,carry=1,表示有进位</span> cur.next = newNode; cur = cur.next; <span class="hljs-keyword">if</span> (cur1 !== <span class="hljs-params">null</span>) { cur1 = cur1.next; } <span class="hljs-keyword">if</span> (cur2 !== <span class="hljs-params">null</span>) { cur2 = cur2.next; } } <span class="hljs-keyword">if</span> (carry > <span class="hljs-params">0</span>) { <span class="hljs-title">// 如果最后还有进位,新加一个节点</span> cur.next = <span class="hljs-keyword">new</span> ListNode(carry); } <span class="hljs-keyword">return</span> dummyHead.next; }; ``` ``` C++ > C++代码与上面的 JavaScript 代码略有不同:将 carry 是否为 0 的判断放到了 while 循环中 ``` <pre class="calibre18">``` <span class="hljs-title">/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */</span> <span class="hljs-keyword">class</span> Solution { <span class="hljs-keyword">public</span>: <span class="hljs-function">ListNode* <span class="hljs-title">addTwoNumbers</span><span class="hljs-params">(ListNode* l1, ListNode* l2)</span> </span>{ ListNode* ret = <span class="hljs-params">nullptr</span>; ListNode* cur = <span class="hljs-params">nullptr</span>; <span class="hljs-keyword">int</span> carry = <span class="hljs-params">0</span>; <span class="hljs-keyword">while</span> (l1 != <span class="hljs-params">nullptr</span> || l2 != <span class="hljs-params">nullptr</span> || carry != <span class="hljs-params">0</span>) { carry += (l1 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">0</span> : l1->val) + (l2 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">0</span> : l2->val); <span class="hljs-keyword">auto</span> temp = <span class="hljs-keyword">new</span> ListNode(carry % <span class="hljs-params">10</span>); carry /= <span class="hljs-params">10</span>; <span class="hljs-keyword">if</span> (ret == <span class="hljs-params">nullptr</span>) { ret = temp; cur = ret; } <span class="hljs-keyword">else</span> { cur->next = temp; cur = cur->next; } l1 = l1 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">nullptr</span> : l1->next; l2 = l2 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">nullptr</span> : l2->next; } <span class="hljs-keyword">return</span> ret; } }; ``` ``` **复杂度分析** - 时间复杂度:O(N)O(N)O(N) - 空间复杂度:O(1)O(1)O(1) ## 拓展 通过单链表的定义可以得知,单链表也是递归结构,因此,也可以使用递归的方式来进行 reverse 操作。 > 由于单链表是线性的,使用递归方式将导致栈的使用也是线性的,当链表长度达到一定程度时,递归会导致爆栈,因此,现实中并不推荐使用递归方式来操作链表。 ### 描述 1. 将两个链表的第一个节点值相加,结果转为 0-10 之间的个位数,并设置进位信息 2. 将两个链表第一个节点以后的链表做带进位的递归相加 3. 将第一步得到的头节点的 next 指向第二步返回的链表 ### C++实现 ``` <pre class="calibre18">``` <span class="hljs-title">// 普通递归</span> <span class="hljs-keyword">class</span> Solution { <span class="hljs-keyword">public</span>: <span class="hljs-function">ListNode* <span class="hljs-title">addTwoNumbers</span><span class="hljs-params">(ListNode* l1, ListNode* l2)</span> </span>{ <span class="hljs-keyword">return</span> addTwoNumbers(l1, l2, <span class="hljs-params">0</span>); } <span class="hljs-keyword">private</span>: <span class="hljs-function">ListNode* <span class="hljs-title">addTwoNumbers</span><span class="hljs-params">(ListNode* l1, ListNode* l2, <span class="hljs-keyword">int</span> carry)</span> </span>{ <span class="hljs-keyword">if</span> (l1 == <span class="hljs-params">nullptr</span> && l2 == <span class="hljs-params">nullptr</span> && carry == <span class="hljs-params">0</span>) <span class="hljs-keyword">return</span> <span class="hljs-params">nullptr</span>; carry += (l1 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">0</span> : l1->val) + (l2 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">0</span> : l2->val); <span class="hljs-keyword">auto</span> ret = <span class="hljs-keyword">new</span> ListNode(carry % <span class="hljs-params">10</span>); ret->next = addTwoNumbers(l1 == <span class="hljs-params">nullptr</span> ? l1 : l1->next, l2 == <span class="hljs-params">nullptr</span> ? l2 : l2->next, carry / <span class="hljs-params">10</span>); <span class="hljs-keyword">return</span> ret; } }; <span class="hljs-title">// (类似)尾递归</span> <span class="hljs-keyword">class</span> Solution { <span class="hljs-keyword">public</span>: <span class="hljs-function">ListNode* <span class="hljs-title">addTwoNumbers</span><span class="hljs-params">(ListNode* l1, ListNode* l2)</span> </span>{ ListNode* head = <span class="hljs-params">nullptr</span>; addTwoNumbers(head, <span class="hljs-params">nullptr</span>, l1, l2, <span class="hljs-params">0</span>); <span class="hljs-keyword">return</span> head; } <span class="hljs-keyword">private</span>: <span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">addTwoNumbers</span><span class="hljs-params">(ListNode*& head, ListNode* cur, ListNode* l1, ListNode* l2, <span class="hljs-keyword">int</span> carry)</span> </span>{ <span class="hljs-keyword">if</span> (l1 == <span class="hljs-params">nullptr</span> && l2 == <span class="hljs-params">nullptr</span> && carry == <span class="hljs-params">0</span>) <span class="hljs-keyword">return</span>; carry += (l1 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">0</span> : l1->val) + (l2 == <span class="hljs-params">nullptr</span> ? <span class="hljs-params">0</span> : l2->val); <span class="hljs-keyword">auto</span> temp = <span class="hljs-keyword">new</span> ListNode(carry % <span class="hljs-params">10</span>); <span class="hljs-keyword">if</span> (cur == <span class="hljs-params">nullptr</span>) { head = temp; cur = head; } <span class="hljs-keyword">else</span> { cur->next = temp; cur = cur->next; } addTwoNumbers(head, cur, l1 == <span class="hljs-params">nullptr</span> ? l1 : l1->next, l2 == <span class="hljs-params">nullptr</span> ? l2 : l2->next, carry / <span class="hljs-params">10</span>); } }; ``` ``` **复杂度分析** - 时间复杂度:O(N)O(N)O(N) - 空间复杂度:O(N)O(N)O(N),其中 N 的空间是调用栈的开销。 大家对此有何看法,欢迎给我留言,我有时间都会一一查看回答。更多算法套路可以访问我的 LeetCode 题解仓库:<https://github.com/azl397985856/leetcode> 。 目前已经 37K star 啦。 大家也可以关注我的公众号《力扣加加》带你啃下算法这块硬骨头。 ![](https://img.kancloud.cn/cf/0f/cf0fc0dd21e94b443dd8bca6cc15b34b_900x500.jpg)